题解 hdu6810 Imperative Meeting
题解 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)>m\land \forall v\in \mbox{son}_u,2s(v)\le m\) 。考虑到 \(u\) 的所有儿子中最多只能有一个儿子 \(v\) 的 \(2s(v)>m\) ,所以设 \(f(u)\) 表示满足 \(2s(u)>m\) 的 \(S\) 的方案数,那么 \(u\) 作为 \(S\) 重心的方案数就是 \(f(u)-\sum_{v\in \mbox{son}_u}f(v)\) 。
如何求 \(f(u)\) ?不难发现 \(f(u)\) 只和 \(\mbox{size}(u)\) 有关,设 \(g(x)=\sum_{2i>m}{x\choose i}{n-x\choose m-i}\) ,那么 \(f(u)=g(\mbox{size}(u))\) , \(g(x)\) 的算法和在做法二中的类似。
找到了重心,就可以开始求距离了,对于 \(u\) 的每一个儿子子树,其中每个点出现在 \(S\) 中的方案数都是相同的,设 \(g_0(x)=\sum_{2i>m}{x-1\choose i-1}{n-x\choose m-i}\) , \(f_0(u)=g_0(\mbox{size}(u))\) ,那么对于 \(u\) 的儿子 \(v_0\) 子树中的任意一个点出现在 \(S\) 中的方案数是 \(f_0(u)-\sum_{v\in \mbox{son}_u,v\ne 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}^m\min(i,m-i){x\choose i}{n-x\choose m-i}\) ,那么答案就是 \(\sum_{i=2}^nf(\mbox{size}(i))\) 。
如何求 \(f(x)\) ?可以把 \(f(x)\) 分成两半计算,即 \(\sum_{i=0}^ti{x\choose i}{n-x\choose m-i}+\sum_{i=t+1}^m(m-i){x\choose i}{n-x \choose m-i}\) ,设前半部分为 \(g(x)\) ,则:
令 \(s(x)=\sum_{i=0}^C{x\choose i}{A-x\choose 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('\n');
}
return 0;
}