• 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;
    }
    
    
  • 相关阅读:
    集算器如何优化SQL计算(2)分组
    集算器如何优化SQL计算(1)动态列
    android DOM解析Xml
    ASP.NET 抓取网页内容
    抓取HTML网页数据
    [转]Android的网络与通信
    Android的三种网络通信方式
    Android通过onDraw实现在View中绘图操作
    Android Canvas类介绍
    DatabaseMetaData的用法(转)
  • 原文地址:https://www.cnblogs.com/pkh68/p/10674683.html
Copyright © 2020-2023  润新知