【2020 Multi-University 4 I】Imperative Meeting 题解
题目大意
有一棵 n 个结点的树,现有 m 个人位于不同的结点,那么要让他们在同一结点相遇的话会有一个最小总路程。而“m个人位于不同结点”共有 (mn) 种情况,求这 (mn) 种情况的最小总路程之和,模 109+7。
m≤n≤106
多测,T≤1000,∑n≤2×106
2s
题解
考虑每条边的贡献,一条边有贡献当且仅当这条边两侧都有人,且贡献等于两侧人数的较小值。形式化地说,假设一条边连接了 x 子树和 y 子树,那么这条边的贡献为:
i=1∑m−1(isizex)(m−isizey)min(i,m−i)
把 min 拆掉变成
i=1∑⌊2m−1⌋i(isizex)(m−isizey)+i=1∑⌊2m−1⌋i(isizey)(m−isizex)+[m为偶数]2m(2msizex)(2msizey)
这式子共 3 项,最后一项对于每条边单独算一下就行了,所以要算前两项,以第一项为例做一个变形把乘 i 去掉:
===i=1∑⌊2m−1⌋i(isizex)(m−isizey)i=1∑⌊2m−1⌋i⋅i!⋅(sizex−i)!sizex!(m−isizey)i=1∑⌊2m−1⌋(i−1)!⋅(sizex−i)!sizex⋅(sizex−1)!(m−isizey)sizexi=1∑⌊2m−1⌋(i−1sizex−1)(m−isizey)
注意到 sizex+sizey=n,记 hs=∑i=1⌊2m−1⌋(i−1s−1)(m−in−s),结果发现这玩意是能递推的!
有两种方法能够得到递推结果。
一种是考虑 hs 的组合意义:把 m−1 个球放到 n−1 个盒子里,每个盒子最多放 1 个球,且要求前 s−1 个盒子最多只能有 ⌊2m−1⌋−1 个球,问方案数。
m−1 个球放入 n−1 个盒子共 (m−1n−1) 种方案,这也是 h1 的值。随着 s 的增大,只会有越来越多的方案变得非法。从 hs−1 到 hs 变得非法的方案,即为前 s−2 个盒子已经放够了 ⌊2m−1⌋−1 个球,而第 s−1 个盒子又放了一个球,其余球放在后面的盒子里,方案数为 (⌊2m−1⌋−1s−2)(m−1−⌊2m−1⌋n−s)。
另一种方法是硬推:
hs+1−hs=i=1∑⌊2m−1⌋(i−1s)(m−in−s−1)−(i−1s−1)(m−in−s)
把 i=1 单独拉出来,其余的把 (i−1s) 拆成 (i−1s−1)+(i−2s−1)、把 (m−in−s) 拆成 (m−in−s−1)+(m−i−1n−s−1),然后基本上所有的项都被消去了,能得到同样的结果。式子太长就不写了。
于是,O(n) 递推出 h,然后每条边单独算贡献,就得到答案了。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| #include<bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std;
typedef long long LL;
const int maxn=1e6+5; const LL mo=1e9+7;
int n,m,mh; vector<int> e[maxn];
LL Pow(LL x,LL y) { LL re=1; for(; y; y>>=1, x=x*x%mo) if (y&1) re=re*x%mo; return re; }
LL fac[maxn],inv[maxn]; void C_Pre(int n) { fac[0]=1; fo(i,1,n) fac[i]=fac[i-1]*i%mo; inv[n]=Pow(fac[n],mo-2); fd(i,n-1,0) inv[i]=inv[i+1]*(i+1)%mo; } LL C(int n,int m) {return (n>=m && m>=0) ?fac[n]*inv[m]%mo*inv[n-m]%mo :0 ;}
void ReadInt(int &data) { data=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); do{ data=(data<<3)+(data<<1)+ch-'0'; ch=getchar(); } while (ch>='0' && ch<='9'); }
LL h[maxn]; void Pre() { int mhv=(m-1)>>1; if (mhv==0) { fo(i,1,n-1) h[i]=0; return; } h[1]=C(n-1,m-1); fo(i,2,n-1) h[i]=(h[i-1]-C(i-2,mhv-1)*C(n-i,m-1-mhv)%mo+mo)%mo; fo(i,1,n-1) (h[i]*=i)%=mo; }
int size[maxn]; void dfs_size(int k,int last) { size[k]=1; for(int son:e[k]) if (son!=last) { dfs_size(son,k); size[k]+=size[son]; } }
LL ans; void dfs_ans(int k,int last) { for(int son:e[k]) if (son!=last) { (ans+=h[size[son]]+h[n-size[son]]+((m&1) ?0 :C(size[son],mh)*C(n-size[son],mh)%mo*mh ))%=mo; dfs_ans(son,k); } }
int T; int main() { C_Pre(1e6); scanf("%d",&T); while (T--) { ReadInt(n), ReadInt(m); mh=m>>1; fo(i,1,n) e[i].clear(); fo(i,2,n) { int x; ReadInt(x); e[x].push_back(i); } Pre(); dfs_size(1,0); ans=0; dfs_ans(1,0); printf("%lld\n",ans); } }
|