題目描述:
給出一棵個點的無邊權無向樹。對每個,求出恰有條邊與原樹重合的個點的樹的數量。
題目分析:
的插值解法已經被吊打了。
首先求出強制條邊相等,剩下的邊任意的方案數,二項式反演/容斥一下可得。於是問題就是求。
而強制條邊相等就是選出個連通塊,於是問題轉化爲求把原樹劃分爲個連通塊後的生成樹方案數。
由prufer序列可知,如果有個連通塊,每個大小是,那麼生成樹的方案數是
於是樹形DP求生成樹方案數,如果不帶就是求劃分個連通塊的方案數,這個可以求,而帶上權值後需要一個小 ,相當於方案數變爲在每個連通塊裏面再選一個點。
設表示子樹內劃分爲個連通塊,所在的連通塊 沒有選/已經選了 點的方案數,那麼合併兩邊的時候個連通塊 + 個連通塊可以貢獻到 個連通塊;或者合併成一個貢獻到個連通塊,合併的時候有情況需要選點,由乘法分配律,相當於左邊選了點*右邊沒選+左邊沒選*右邊選了的方案數。(口胡,詳見代碼)
Code:
#include<bits/stdc++.h>
#define maxn 105
using namespace std;
const int mod = 1e9+7;
int n,f[maxn][maxn][2],g[maxn][2],ans[maxn],siz[maxn],C[maxn][maxn];
int fir[maxn],nxt[maxn<<1],to[maxn<<1],tot;
void line(int x,int y){nxt[++tot]=fir[x],fir[x]=tot,to[tot]=y;}
#define add(x,y) x=(x+(y))%mod
void dfs(int u,int ff){
siz[u]=f[u][1][0]=f[u][1][1]=1;
for(int i=fir[u],v;i;i=nxt[i]) if((v=to[i])!=ff){
dfs(v,u);
memcpy(g,f[u],sizeof f[u]),memset(f[u],0,sizeof f[u]);
for(int j=1;j<=siz[u];j++)
for(int k=1;k<=siz[v];k++){
add(f[u][j+k][0],1ll*g[j][0]*f[v][k][1]);
add(f[u][j+k][1],1ll*g[j][1]*f[v][k][1]);
add(f[u][j+k-1][0],1ll*g[j][0]*f[v][k][0]);
add(f[u][j+k-1][1],1ll*g[j][1]*f[v][k][0]+1ll*g[j][0]*f[v][k][1]);
}
siz[u]+=siz[v];
}
}
int Pow(int a,int b){int s=1;for(;b;b>>=1,a=1ll*a*a%mod) b&1&&(s=1ll*s*a%mod);return s;}
int main()
{
scanf("%d",&n);
for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),line(x,y),line(y,x);
dfs(1,0);
for(int i=1;i<=n;i++) ans[n-i]=1ll*f[1][i][1]*(i>1?Pow(n,i-2):Pow(n,mod-2))%mod;
for(int i=0;i<n;i++) for(int j=C[i][0]=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++) add(ans[i],1ll*(j-i&1?-1:1)*C[j][i]*ans[j]);
printf("%d%c",(ans[i]+mod)%mod,i==n-1?10:32);
}
}