题解 hdu6810 Imperative Meeting
题目描述
给定一棵 (n) 个点的树,每条边长度为 (1) ,定义点集 (S) 的函数 (f(S)) :
(说人话就是 (S) 到它们的重心的距离和)
给定 (m) ,求 (sum_{|S|=m}f(S)) 。
题目分析
做法一
由于某个点集 (S) 的重心可能不唯一,所以我们先以 (1) 为根建立一棵有根树,然后钦定点集 (S) 选的重心要是深度最小的点,这样重心就是唯一的。
设点 (u) 的子树大小为 (mbox{size}(u)) ,某个点集 (S) 在 (u) 内的点的数量为 (s(u)),那么 (u) 是 (S) 的重心当且仅当 (2s(u)>mland forall vin mbox{son}_u,2s(v)le m) 。考虑到 (u) 的所有儿子中最多只能有一个儿子 (v) 的 (2s(v)>m) ,所以设 (f(u)) 表示满足 (2s(u)>m) 的 (S) 的方案数,那么 (u) 作为 (S) 重心的方案数就是 (f(u)-sum_{vin mbox{son}_u}f(v)) 。
如何求 (f(u)) ?不难发现 (f(u)) 只和 (mbox{size}(u)) 有关,设 (g(x)=sum_{2i>m}{xchoose i}{n-xchoose m-i}) ,那么 (f(u)=g(mbox{size}(u))) , (g(x)) 的算法和在做法二中的类似。
找到了重心,就可以开始求距离了,对于 (u) 的每一个儿子子树,其中每个点出现在 (S) 中的方案数都是相同的,设 (g_0(x)=sum_{2i>m}{x-1choose i-1}{n-xchoose m-i}) , (f_0(u)=g_0(mbox{size}(u))) ,那么对于 (u) 的儿子 (v_0) 子树中的任意一个点出现在 (S) 中的方案数是 (f_0(u)-sum_{vin mbox{son}_u,v e v_0}f(v)-f_0(v_0)) ,那么 (v_0) 子树对于以 (u) 为重心时的点集的贡献就是 (v_0) 子树中所有点到 (u) 的距离再乘以出现的方案数,于是就可以求答案了。对于 (u) 的父亲节点所在的子树用类似的方法即可。
感觉这种做法特别复杂,还有一个比较简单的方法。
做法二
做法一是按点来算贡献的,考虑换一种方法,按边来算贡献。
对于一条边,如果它连接着的两边的点的数量分别是 (a) 和 (b) ,那么它的贡献就是 (min(a,b)) ,以 (1) 为根建树,设此时每个点的子树大小为 (mbox{size}()) , (f(x)=sum_{i=0}^mmin(i,m-i){xchoose i}{n-xchoose m-i}) ,那么答案就是 (sum_{i=2}^nf(mbox{size}(i))) 。
如何求 (f(x)) ?可以把 (f(x)) 分成两半计算,即 (sum_{i=0}^ti{xchoose i}{n-xchoose m-i}+sum_{i=t+1}^m(m-i){xchoose i}{n-x choose m-i}) ,设前半部分为 (g(x)) ,则:
令 (s(x)=sum_{i=0}^C{xchoose i}{A-xchoose B-i}) ,考虑递推 (s(x)) :
于是就可以算 (f(x)) 了。
参考代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ch() getchar()
#define pc(x) putchar(x)
using namespace std;
template<typename T>void read(T&x){
static char c;static int f;
for(c=ch(),f=1;c<'0'||c>'9';c=ch())if(c=='-')f=-f;
for(x=0;c>='0'&&c<='9';c=ch())x=x*10+(c&15);x*=f;
}
template<typename T>void write(T x){
static char q[65];int cnt=0;
if(x<0)pc('-'),x=-x;
q[++cnt]=x%10,x/=10;
while(x)
q[++cnt]=x%10,x/=10;
while(cnt)pc(q[cnt--]+'0');
}
const int maxn=1000005,mod=1000000007;
int mo(const int x){
return x>=mod?x-mod:x;
}
int iac[maxn],fac[maxn];
int binom(int n,int m){
return n<0||m<0||m>n?0:1ll*iac[m]*iac[n-m]%mod*fac[n]%mod;
}
int f[maxn];
int fa[maxn],sz[maxn];
int main(){
fac[0]=fac[1]=iac[0]=iac[1]=1;
for(int i=2;i<maxn;++i)
iac[i]=1ll*(mod-mod/i)*iac[mod%i]%mod;
for(int i=2;i<maxn;++i)
iac[i]=1ll*iac[i-1]*iac[i]%mod,fac[i]=1ll*fac[i-1]*i%mod;
int T;read(T);
while(T--){
int n,m;read(n),read(m);
int mx=(m-1)/2;
f[1]=(mx==0?0:binom(n-1,m-1));
for(int i=2;i<n;++i)
f[i]=mo(mod-1ll*binom(i-2,mx-1)*binom(n-i,m-mx-1)%mod+f[i-1]);
for(int i=1;i<n;++i)
f[i]=1ll*f[i]*i%mod;
for(int i=1;i<=n-i;++i){
f[i]=f[n-i]=mo(f[i]+f[n-i]);
if(!(m&1))f[i]=f[n-i]=mo(f[i]+1ll*binom(i,mx+1)*binom(n-i,mx+1)%mod*(mx+1)%mod);
}
for(int i=2;i<=n;++i){
sz[i]=1;read(fa[i]);
}
sz[1]=1;int ans=0;
for(int i=n;i>=2;--i){
sz[fa[i]]+=sz[i];
ans=mo(ans+f[sz[i]]);
}
write(ans),pc('
');
}
return 0;
}