AT_tdpc_tree 木

发布时间 2023-11-15 11:35:22作者: Candycar

题目描述:

给定一棵大小为 \(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;
}