题解:P15578 [USACO26FEB] Random Tree Generation G
Grammar_hbw · · 题解
第一步随机得到的树必然满足父亲的编号小于儿子这个条件。
第二步随机的意义就是将第一步的树的编号用一个排列
对这类树计数,最后除以总方案数
不妨先指定一个根,设
也就是说,
。
对于每一个点,钦定它是根,算出来的
:::info{Code Time}
#include <bits/stdc++.h>
using namespace std;
const int N=1000007,mod=1000000007;
int fa[N],fi[N],f[N],sz[N],ans;
vector<int> to[N];
int inv(int x){
int ans=1,b=mod-2;
for(;b;b>>=1,x=1ll*x*x%mod) if(b&1) ans=1ll*ans*x%mod;
return ans;
}
void dfs(int o,int p){
sz[o]=1;
for(auto i:to[o]) if(i!=p) dfs(i,o),sz[o]+=sz[i];
f[o]=fa[sz[o]-1];
for(auto i:to[o]) if(i!=p) f[o]=1ll*f[o]*f[i]%mod*fi[sz[i]]%mod;
}
void calc(int o,int p){
ans=(ans+f[o])%mod;
for(auto i:to[o]) if(i!=p){
int bfo=f[o],bso=sz[o],bfi=f[i],bsi=sz[i]; // 备份可能用到的数。
f[o]=1ll*f[o]*fi[sz[o]-1]%mod*inv(f[i])%mod*fa[sz[i]]%mod;
sz[o]-=sz[i];
f[o]=1ll*f[o]*fa[sz[o]-1]%mod;
f[i]=1ll*f[i]*fi[sz[i]-1]%mod; // 换根的通法:将 o 的一个儿子 i 的贡献去掉,然后把 i 加上 o 的贡献,dfs,最后还原。
sz[i]+=sz[o];
f[i]=1ll*f[i]*fa[sz[i]-1]%mod*f[o]%mod*fi[sz[o]]%mod;
calc(i,o);
f[o]=bfo,sz[o]=bso,f[i]=bfi,sz[i]=bsi;
}
}
int main(){
cin.tie(0)->sync_with_stdio(0);
fa[0]=1;
for(int i=1;i<N;i++) fa[i]=1ll*i*fa[i-1]%mod;
fi[N-1]=inv(fa[N-1]);
for(int i=N-1;i;i--) fi[i-1]=1ll*i*fi[i]%mod;
int t;
cin>>t;
while(t-->0){
int n;
cin>>n;
for(int i=1;i<=n;i++) to[i].clear(); // 多测要清空。
for(int i=1,u,v;i<n;i++) cin>>u>>v,to[u].push_back(v),to[v].push_back(u);
ans=0;
dfs(1,0);
calc(1,0);
cout<<1ll*ans*fi[n]%mod*fi[n-1]%mod<<'\n'; //求的是概率,记得除以总方案数。
}
return 0;
}
:::