传送门
CayleyCayleyCayley公式:一个完全图有nn−2n^{n-2}nn−2棵无根生成树(可用prufer序列证明)
扩展CayleyCayleyCayley公式:被确定边分为大小为a1,a2,⋯,ama_1,a_2,\cdots, a_ma1,a2,⋯,am的连通块,则有nm−2∏i=1main^{m-2}\prod\limits_{i=1}^{m}{a_i}nm−2i=1∏mai种生成树
我们强制生成树中的一些边与原树中的边相同,求出生成树中至少有xxx条边于原树中边相同的方案数ansxans_xansx,再容斥得到生成树中恰好有xxx条边与原树中边相同的方案数AnsxAns_xAnsx。
Ansx=∑i=x(−1)i−x(ix)ansiAns_x=\sum\limits_{i=x}(-1)^{i-x}\dbinom{i}{x}ans_iAnsx=i=x∑(−1)i−x(xi)ansi
考虑一棵以uuu为根的生成子树,里面有jjj条边强制与原树中边相同。如果只加入强制相同的jjj条边,我们会得到若干连通块,即子树被非强制相同的边分成若干连通块,可以用拓展CayleyCayleyCayley公式统计方案数。
nm−2n^{m-2}nm−2可以留到最后处理,所以我们只考虑求∏i=1mai\prod\limits_{i=1}^{m}{a_i}i=1∏mai,其组合意义为每个连通块内各选一个关键点的方案数。
设dp[u][j][0/1]dp[u][j][0/1]dp[u][j][0/1]表示以uuu为根的子树内有jjj条强制相同的边,uuu所在的连通块选/未选选/未选选/未选关键点 的选关键点方案数之和。转移如下:
若u−vu-vu−v是强制相同的边(v∈sonuv\in son_uv∈sonu):
dp[u][j][0]×dp[v][k][0]→dp[u][j+k+1][0]dp[u][j][0]\times dp[v][k][0]\to dp[u][j+k+1][0]dp[u][j][0]×dp[v][k][0]→dp[u][j+k+1][0]
dp[u][j][0]×dp[v][k][1]+dp[u][j][1]×dp[v][k][0]→dp[u][j+k+1][1]dp[u][j][0]\times dp[v][k][1]+dp[u][j][1]\times dp[v][k][0]\to dp[u][j+k+1][1]dp[u][j][0]×dp[v][k][1]+dp[u][j][1]×dp[v][k][0]→dp[u][j+k+1][1]
若u−vu-vu−v是非强制相同的边(v∈sonuv\in son_uv∈sonu):
dp[u][j][0]×dp[v][k][1]→dp[u][j+k][0]dp[u][j][0]\times dp[v][k][1]\to dp[u][j+k][0]dp[u][j][0]×dp[v][k][1]→dp[u][j+k][0]
dp[u][j][1]×dp[v][k][1]→dp[u][j+k][1]dp[u][j][1]\times dp[v][k][1]\to dp[u][j+k][1]dp[u][j][1]×dp[v][k][1]→dp[u][j+k][1]
类似题目:WC2019数树
#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
const int N=8010;
int read(){int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int mul(int x,int y){return 1ll*x*y%mod;}
void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
void Mul(int &x,int y){x=1ll*x*y%mod;}
int ksm(int a,int b){if(b<0) return ksm(ksm(a,-b),mod-2);int ans=1;while(b){if(b&1) ans=mul(ans,a);a=mul(a,a);b>>=1;}return ans;
}
int n,fac[N],ifac[N];
int cnt,head[N],nxt[N<<1],to[N<<1],siz[N];
vector<int>f[N],g[N];//f:0,g:1
void adde(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;
}
void Clear(vector<int> &v){vector<int>().swap(v);
}
void dfs(int u,int fa){siz[u]=1;f[u].push_back(1);g[u].push_back(1);for(int i=head[u];i;i=nxt[i]){int v=to[i];if(v==fa) continue;dfs(v,u);f[u].resize(siz[u]+siz[v]);g[u].resize(siz[u]+siz[v]);for(int j=siz[u]-1;j>=0;j--){const int fu=f[u][j],gu=g[u][j];f[u][j]=g[u][j]=0;for(int k=siz[v]-1;k>=0;k--){const int fv=f[v][k],gv=g[v][k];Add(f[u][j+k+1],mul(fu,fv));Add(f[u][j+k],mul(fu,gv));Add(g[u][j+k+1],add(mul(fu,gv),mul(gu,fv)));Add(g[u][j+k],mul(gu,gv));}}siz[u]+=siz[v];Clear(f[v]),Clear(g[v]);}
}
int C(int n,int m){return mul(mul(fac[n],ifac[m]),ifac[n-m]);
}
int main(){n=read();for(int i=1;i<n;i++){int u=read(),v=read();adde(u,v),adde(v,u);}dfs(1,0);fac[0]=1;for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);ifac[n]=ksm(fac[n],mod-2);for(int i=n;i>=1;i--) ifac[i-1]=mul(ifac[i],i);for(int i=0;i<n;i++) Mul(g[1][i],ksm(n,n-i-2));for(int i=0;i<n;i++){int ans=0;for(int j=i;j<n;j++){if((j-i)&1) Dec(ans,mul(C(j,i),g[1][j]));else Add(ans,mul(C(j,i),g[1][j]));}printf("%d ",ans);}return 0;
}