[CF1097G Vladislav and a Great Legend ]
Code
#include<bits/stdc++.h>
#define rep(i,a,b) for(ll i=(a);i<=(b);i++)
#define per(i,a,b) for(ll i=(a);i>=(b);i--)
#define ll long long
using namespace std;
const ll mod=1e9+7;
const ll N=2e5+100;
const ll K=300;
struct node{ll y,n;}e[N<<1];
ll dp[N][K],s[K][K],lin[N<<1],fact[K],size[N],g[K],f[K],len=1,ans=0,n,k,x,y;
void read(ll x,ll y)
{e[++len].y=y,e[len].n=lin[x],lin[x]=len;}
void init(){
fact[0]=1;rep(i,1,k)fact[i]=i*fact[i-1]%mod;
s[0][0]=1;
rep(i,1,k){
rep(j,1,i){
s[i][j]=(s[i-1][j-1]+s[i-1][j]*j%mod)%mod;
}
}
}
void update(ll &x,ll y){x=((x+y)%mod+mod)%mod;}
void dfs(ll x,ll fa){
size[x]=1,dp[x][0]=2;
for(ll i=lin[x];i;i=e[i].n){
ll y=e[i].y;
if(y==fa)continue;
dfs(y,x);
memset(f,0,sizeof(f));
rep(p,0,min(k,size[x])){
rep(q,0,min(k-p,size[y]))
update(f[p+q],1LL*dp[x][p]*dp[y][q]%mod);
}
rep(j,0,k)update(g[j],-dp[y][j]);
memcpy(dp[x],f,sizeof(f));
size[x]+=size[y];
}
rep(i,0,k)update(g[i],dp[x][i]);
per(i,k,1)update(dp[x][i],dp[x][i-1]);
update(dp[x][1],-1);
}
int main()
{
scanf("%lld%lld",&n,&k);
rep(i,2,n){scanf("%d%d",&x,&y);read(x,y),read(y,x);}
init(); dfs(1,0);
rep(i,0,k)ans=(ans+1LL*fact[i]*s[k][i]%mod*1LL*g[i]%mod)%mod;
printf("%lld\n",ans);
return 0;
}
- g[i]表示对于所有生成树,从中标记了i条边的方案
- dp[x][i]表示生成树所有点的LCA在x时,从这些生成树中选择i条边的方案数,生成树包括 含有x->fa[x]边 的树
- dp[x][0]=2,对于任何一个孤立的点,可以选或者不选,有2种情况
- 当f[p+q]<-dp[x][p]*dp[y][q] (q==0),这时由于y点对选出来的边贡献为零,所以y点选或不选都对应了相同的情况,所以这一点计算了两次,在后面要减去重复的 g[j]-=dp[y][j];
- x向fa[x]连边的情况也要算进答案,所以dp[x][i]+=dp[x][i-1]
- 当选出来一条边时,dp[x][1]+=dp[x][0],dp[x][0]有一种情况是x这个点不选,这种情况是不合法的,所以dp[x][1]-=1