CF1097G Vladislav and a Great Legend

CF1097G Vladislav and a Great Legend

题面:Luogu

解析

[sum_{X subseteq {1,2...n}} f(X)^k ]

[=sum_{X} sum_{i=0}^{k} S(k,i) imes i! imes {f(X) choose i} ]

[=sum_{i=0}^{k} S(k,i) imes i! imes sum_{X} {f(X) choose i} ]

考虑计算后面的式子,从组合意义去考虑,那么后面的式子等价于枚举大小为(i)的边集(S),计算有多少选出的点集(X)的生成树中包含的方案数之和。

不妨以点集(X)中深度最小的点(x)来计算贡献,设(f(x,i))表示在以(x)为根的子树中选出大小为(i)的边集(S),而生成树包含(S)的点集的方案数之和,不难发现大小为(i)的边集(S)的贡献即为(g(i)=sum_{x=1}^n f(1,i))

那么如何计算(f(x,i))呢?我们考虑枚举(i)条边如何分配给(x)的儿子,不难发现生成树就是由若干颗生成树拼合而成,那么直接树上背包统计即可。先考虑初始情况,即(f(x,0))。根据定义,有(f(x,0)=2),即不选(x)和选(x)。对于(f(x,i)(i gt 0)),有(f'(x,k)=sum_{k=i+j} f(x,i) imes f(v,j))。注意这里的(f(v,i)=f(v,i)+f(v,i-1)),因为要枚举是否选择(x)(v)的边。然而这样有两种不合法的情况,一种是子树内没有选点(即(f(v,0))中的一种情况),这样是不能统计到(f(v,1))的,所以(f(v,1))要减掉1,另一种是子树(v)外没有选点,但是却选择了(x)(v)的边,这种情况要减掉多统计的(f(v,i-1))

代码


#include<cstdio>
#define N 100005
#define K 205

using namespace std;

const int P=1e9+7;

inline int In(){
	char c=getchar(); int x=0,ft=1;
	for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
	for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
	return x*ft;
}

inline int min(int a,int b){
	return a<b?a:b;
}

int n,k_p,h[N],e_tot=0;
struct E{ int to,nex; }e[N<<1];

inline void add(int u,int v){
	e[++e_tot]=(E){v,h[u]}; h[u]=e_tot;
}

int S[K][K],fac[K];

inline void Get_S_fac(){
	fac[0]=1; for(int i=1;i<=k_p;++i) fac[i]=1ll*i*fac[i-1]%P;
	S[0][0]=1;
	for(int i=1;i<=k_p;++i)
	for(int j=1;j<=i;++j)
	S[i][j]=(S[i-1][j-1]+1ll*j*S[i-1][j]%P)%P;
}

int sz[N],f[N][K],ans=0,t1[K],g[K];

void dp(int u,int pre){
	f[u][0]=2; sz[u]=1;
	for(int c=h[u],v;c;c=e[c].nex){
		if((v=e[c].to)==pre) continue; dp(v,u);
		for(int i=0;i<=min(k_p,sz[u]+sz[v]-1);++i) t1[i]=0;
		for(int i=0;i<sz[u]&&i<=k_p;++i)
		for(int j=0;j<=sz[v]&&i+j<=k_p;++j)
		t1[i+j]=(t1[i+j]+1ll*f[u][i]*f[v][j]%P)%P;
		sz[u]+=sz[v];
		for(int i=0;i<sz[u]&&i<=k_p;++i) f[u][i]=t1[i];
	}
	if(u==1) for(int i=0;i<=k_p;++i) g[i]=(g[i]+f[u][i])%P;
	else{
		for(int i=1;i<=k_p;++i) g[i]=(g[i]-f[u][i-1]+P)%P;
		g[1]=(g[1]+1)%P;
	}
	for(int i=k_p;i;--i) f[u][i]=(f[u][i]+f[u][i-1])%P;
	f[u][1]=(f[u][1]+P-1)%P;
}

int main(){
	n=In(); k_p=In();
	for(int i=1,u,v;i<n;++i){
		u=In(); v=In();
		add(u,v); add(v,u);
	}
	dp(1,-1); Get_S_fac();
	for(int i=0;i<=k_p;++i)
	ans=(ans+1ll*S[k_p][i]*fac[i]%P*g[i]%P)%P;
	printf("%d
",ans);
	return 0;
}

原文地址:https://www.cnblogs.com/pkh68/p/10674683.html