题解 hdu6810 Imperative Meeting

题解 hdu6810 Imperative Meeting

题目链接

题目描述

给定一棵 \(n\) 个点的树,每条边长度为 \(1\) ,定义点集 \(S\) 的函数 \(f(S)\)

\[f(S)=\min_{u=1}^n(\sum_{v\in S}\mbox{dis}(u,v)) \]

(说人话就是 \(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)\) ,则:

\[g(x)=\sum_{i=0}^ti{x\choose i}{n-x\choose m-i}\\ =\sum_{i=0}^tx{x-1\choose i-1}{n-x\choose m-i}\\ \frac{g(x)}{x}=\sum_{i=0}^t{x-1\choose i-1}{n-x\choose m-i} \]

\(s(x)=\sum_{i=0}^C{x\choose i}{A-x\choose B-i}\) ,考虑递推 \(s(x)\)

\[s(x)=\sum_{i=0}^C{x\choose i}{A-x\choose B-i}\\ =\sum_{i=0}^C[{x-1\choose i-1}+{x-1\choose i}][{A-x+1\choose B-i}-{A-x\choose B-i-1}]\\ =\sum_{i=0}^C{x-1\choose i}{A-x+1\choose B-i}+\sum_{i=0}^C{x-1\choose i-1}{A-x\choose B-i}-\sum_{i=0}^C{x-1\choose i}{A-x\choose B-i-1}\\ =s(x-1)+\sum_{i=-1}^{C-1}{x-1\choose i}{A-x\choose B-i-1}-\sum_{i=0}^C{x-1\choose i}{A-x\choose B-i-1}\\ =s(x-1)+{x-1\choose -1}{A-x\choose B}-{x-1\choose C}{A-x\choose B-C-1} \]

于是就可以算 \(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;
}

posted @ 2021-01-19 20:41  xiaolilsq  阅读(89)  评论(0编辑  收藏  举报