题目描述:
给定一棵大小为 \(n\) 的树,用另外 \(n\) 个点加边构造出这棵树,要求构造时所被边连到的点联通,求有多少连边顺序。
数据范围:
\(1 \leq n \leq 1000\)。
思路:
首先我们发现,因为题目要求连边后一定是一个连通块,所以考虑以哪一个点作为起点,然后向下连边。
所以我们得到一个初步的思路:枚举点 \(u\) 求出以 \(u\) 为根的连边方案数
然后我们就思考一下怎么处理这个问题?
考虑树形 \(Dp\):令 \(f_u\) 表示以 \(u\) 为根的子树连边的方案数是多少。
假设我们已经算出了 \(f_v\),考虑怎么将 \(f_v\) 合并到 \(f_u\) 中。
\(u\) 的儿子之间的连边其实是独立的,也就是说如果我们确定了 \(v\) 中的连边顺序,则只要这个连边的相对顺序不变,就可以成为一个合法的方案。
所以假设枚举到了 \(v\),并且当前这些子树中的边的数量为 \(sz_u\),子树 \(v\) 中的边的数量为 \(sz_v\)
则方案数就是 \(\binom{sz_u}{sz_v}\),即从 \(sz_u\) 条边中,选 \(sz_v\) 个位置放 \(v\) 子树内填的边。
所以可以得出 \(f_u\gets f_v\times \binom{sz_u}{sz_v}\)
注意的是,这里的 \(sz_u\) 都是指的是边数,而不是点数。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=1005;
const int mod=1e9+7;
int fac[maxn],inv[maxn];
int qp(int x,int k){
int res=1;
while(k){
if(k&1)res=res*x%mod;
x=x*x%mod;
k>>=1;
}
return res;
}
void init(){
int N=maxn-5;
fac[0]=1;
for(int i=1;i<=N+1;i++)fac[i]=fac[i-1]*i%mod;
inv[N+1]=qp(fac[N+1],mod-2);
for(int i=N;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
return ;
}
int C(int n,int m){
if(n<m||m<0)return 1;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int n;
vector<int>G[maxn];
int sz[maxn],dep[maxn],f[maxn];
void dfs(int u,int fa){
f[u]=1;
sz[u]=0;
for(int v:G[u]){
if(v==fa)continue;
dfs(v,u);
sz[u]+=sz[v];
f[u]=f[u]*f[v]%mod*C(sz[u],sz[v])%mod;
}
sz[u]++;
return ;
}
signed main(){
cin>>n;
for(int i=1;i<n;i++){
int u,v;
cin>>u>>v;
G[u].push_back(v);
G[v].push_back(u);
}
init();
int ans=0;
for(int i=1;i<=n;i++){
dfs(i,0);
ans=(ans+f[i])%mod;
}
cout<<ans*qp(2,mod-2)%mod<<endl;
return 0;
}
标签:sz,连边,int,res,tree,tdpc,maxn,mod
From: https://www.cnblogs.com/Candycar/p/17833460.html