Atcoder ABC 160 F - Distributing Integers 换根dp+概率
Atcoder ABC 160 F - Distributing Integers
题意
给一棵树,从一个点出发每次可以向相邻的点传递一次,第i次传递到的点编号为i,问分别从1--n点出发编号有多少种
题解
首先1--n点出发分别算出值可以看出是个换根dp,那么最重要的是怎么求出指定一个根它的值。这题其实就是求一个拓扑序,对于一个n个点的数来说,排列一共有\(n!\)个那么在这所有排列中,第一个点一定标号为1,也就是说\(n!\)个点中。\(1/n\)的可能性达成上述目标,也就是说有\((n-1)!\)个序列是满足条件的,那对于它的子节点如果算呢,如果一个子节点的子树大小为sz,那么有\(1/sz\)的可能性满足子树的根在第一个,因为概率是互相独立的,所以对于一个点来说,合法的序列为\(\frac{n!}{\Sigma_{1}^nsz[i]}\),而题目要求1-n的,换一下根即可
逆元,阶乘逆元均可打表
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define F first
#define S second
#define mkp make_pair
#define pii pair<int,int>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+5;
const int mod=1e9+7;
int fac[maxn],inv[maxn],dp[maxn],sz[maxn];
int n;
vector<int>v[maxn];
ll mul(ll a,ll b){
return (a*b)%mod;
}
void init(int n){
fac[1]=1;
fac[0]=1;
for(int i=2;i<=n;i++){
fac[i]=mul(fac[i-1],i);
}
inv[1] = 1;
for(int i = 2; i <=n; ++ i)
inv[i] = mul((mod - mod / i) , inv[mod % i] );
}
void dfs(int x,int fa){
sz[x]=1;
for(auto y:v[x]){
if(y==fa)continue;
dfs(y,x);
sz[x]+=sz[y];
}
}
void reroot(int x,int fa){
dp[x]=mul(mul(dp[fa],sz[x]),inv[(n-sz[x])]);
for(auto p:v[x]){
if(p==fa)continue;
reroot(p,x);
}
}
int main(){
scanf("%d",&n);
init(n);
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].pb(y);
v[y].pb(x);
}
dfs(1,-1);
dp[1]=fac[n];
for(int i=1;i<=n;i++){
dp[1]=mul(dp[1],inv[sz[i]]);
}
for(auto p:v[1]){
reroot(p,1);
}
for(int i=1;i<=n;i++)printf("%d\n",dp[i]);
return 0;
}