【Codeforces 1097G】Vladislav and a Great Legend

Description

原题链接

Analysis

∑ X f ( X ) k = ∑ X ∑ i = 0 k S ( k , i ) i ! ( f ( X ) i ) \sum_{X}f(X)^k=\sum_X \sum_{i=0}^k S(k,i) i! {f(X) \choose i} Xf(X)k=Xi=0kS(k,i)i!(if(X))
考虑对于i=1…k,dp出 ( f ( X ) i ) {f(X) \choose i} (if(X))
dp选出哪些边,对于一个特定的选法,它的贡献是可能选出这些边的点集总数(虚树总数)
观察一条边什么时候会出现在虚树中,当且仅当这条边两端的连通块都至少有一个点在点集中
以1为根,那么假设一条边子树内没有其它选择的边,子树大小为s,那么子树内的贡献为 2 s − 1 2^{s}-1 2s1
如果一个边的选法中,其它所有边都在最顶上一条边的子树内,类似地设最顶上那条边的外面结点数为s,外面贡献为 2 s − 1 2^s-1 2s1
其它点当然都是可选可不选
对于一个特定的选法,考虑在边的lca处统计,那么我们只需要做树形背包即可
时间复杂度为O(nk)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<assert.h>
#define fo(i,a,b) for(int i=(a);i<=(b);++i)
#define fd(i,b,a) for(int i=(b);i>=(a);--i)
#define bfo(i,v,u) for(int i=BB[v],u=B[i][1];i;u=B[i=B[i][0]][1])
#define mset(a,x) memset(a,x,sizeof(a))
#define mcpy(a,b) memcpy(a,b,sizeof(b))
using namespace std;
typedef long long ll;
int read(){int n=0,p=1;char ch;for(ch=getchar();ch<'0' || ch>'9';ch=getchar())if(ch=='-') p=-1;for(;'0'<=ch && ch<='9';ch=getchar()) n=n*10+ch-'0';return n*p;}
const int N=1e5+5,K=205,mo=1e9+7;
ll qmi(ll x,ll n)
{
	ll t=1;
	for(x%=mo;n;n>>=1,x=x*x%mo) if(n&1) t=t*x%mo;
	return t;
}
void inc(ll &x,ll y){x=(x+y)%mo;}
int n,k,size[N],B0,BB[N],B[N<<1][2];
void link(int u,int v){B[++B0][1]=v,B[B0][0]=BB[u],BB[u]=B0;}
ll _2[N],h[K],f[N][K],g[N][K],sum[K],s[K][K],fac[K];
void dfs(int v,int fr=0)
{
	f[v][0]=2,g[v][0]=2;
	size[v]=1;
	bfo(i,v,u) if(u!=fr)
	{
		dfs(u,v);
		fo(x,0,min(size[v]+size[u],k)) h[x]=0;
		fo(x,0,min(size[u],k))
		{
			if(x) inc(sum[x+1],f[u][x]*(_2[n-size[u]]-1));
			else inc(sum[1],(_2[size[u]]-1)*(_2[n-size[u]]-1));
			fo(y,0,min(size[v],k-x))
			{
				inc(h[x+y],f[u][x]*f[v][y]);
				if(x) inc(h[x+y+1],f[u][x]*f[v][y]);
				else inc(h[y+1],(_2[size[u]]-1)*f[v][y]);
			}
		}
		size[v]+=size[u];
		fo(x,0,min(size[v],k)) f[v][x]=h[x];
	}
	bfo(i,v,u) if(u!=fr)
		fo(x,0,min(size[u],k))
			if(x)
			{
				inc(g[v][x],f[u][x]*_2[size[v]-size[u]]);
				inc(g[v][x+1],f[u][x]*_2[size[v]-size[u]]);
			}
			else
			inc(g[v][1],(_2[size[u]]-1)*_2[size[v]-size[u]]);
	fo(i,2,k)
		inc(sum[i],(f[v][i]-g[v][i])*_2[n-size[v]]);
}
int main()
{
	//freopen("g.in","r",stdin);
	//freopen("g.out","w",stdout);
	int x,y;
	n=read(),k=read();
	_2[0]=1;fo(i,1,n) _2[i]=_2[i-1]*2%mo;
	fo(i,1,n-1) x=read(),y=read(),link(x,y),link(y,x);
	dfs(1);
	sum[0]=_2[n];
	fac[0]=1;
	fo(i,1,k) fac[i]=fac[i-1]*i%mo;
	s[0][0]=1;
	fo(i,1,k)
		fo(j,1,i) s[i][j]=(s[i-1][j]*j+s[i-1][j-1])%mo;
	ll ans=0;
	fo(j,0,k)
	{
		ll t=s[k][j]*fac[j]%mo*sum[j]%mo;
		ans=(ans+t)%mo;
	}
	ans=(ans%mo+mo)%mo;
	printf("%I64d\n",ans);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值