AT_abc160_f [ABC160F] Distributing Integers
AT_abc160_f [ABC160F] Distributing Integers
题面
求以每个点为根可以有多少种方案,使得对于每个点其父节点编号小于子节点
思路
换根都写题面上了,所以我们考虑如何以一点为根 dp。
容易想到,这是个很经典的 dp。也就是设 \(dp_{u}\) 表示 \(u\) 的子树内的方案数。考虑如何合并,假设现在有 \(sm_{u}\) 个点,然后又一个儿子有 \(sm_{v}\),那么就相当于将 \(sm_{v}\) 个不区分小球放入 \(sm_{u}\) 个可区分盒子里,然后这不可区分球再有 \(dp_{v}\) 个排序方案。所以最终转移就是:
\[dp_u(^{sm_{u}+sm_{v}-1}_{\ \ \ \ \ sm_{u}-1})dp_v \to dp_u
\]
换根就是模板的换根,也就是将父亲中关于自己子树的贡献剔除,如将 \(sm_{u}\) 减少和 \(dp_{u}\) 处以对应贡献,然后把父亲当做儿子去转移即可。
代码
#include <bits/stdc++.h>
using namespace std;
// typedef long long ll;
#define int long long
typedef long double ld;
#define freop(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);
#define endl '\n'
#define inf 0x7f7f7f7f
#define lb(x) ((x)&-(x))
#define pii pair<int,int>
#define fir first
#define sec second
#define mkp(a,b) make_pair(a,b)
const int N=2e5+10,mod=1e9+7;
int n,sm[N],fac[N]={1},inv[N],dp[N];
vector<int> g[N];
inline int C(int n,int m){return fac[n]*inv[m]%mod*inv[n-m]%mod;}
inline int ksm(int a,int b=mod-2){
int c=1;
while(b){
if(b&1) c=c*a%mod;
b>>=1,a=a*a%mod;
}
return c;
}
void dfs1(int u,int fa){
dp[u]=sm[u]=1;
for(int v:g[u]) if(v^fa)
dfs1(v,u),
dp[u]=dp[u]*C(sm[u]+sm[v]-1,sm[u]-1)%mod*dp[v]%mod,
sm[u]+=sm[v];
}
void dfs2(int u,int fa){
int tsm=sm[fa]-sm[u],tdp=dp[fa]*ksm(dp[u])%mod*ksm(C(tsm+sm[u]-1,tsm-1))%mod;
// cerr << u << ':' << dp[u] << ' ' << sm[u] << " " << fa << ':' << tdp << ' ' << tsm << endl;
dp[u]=dp[u]*C(sm[u]+tsm-1,sm[u]-1)%mod*tdp%mod,sm[u]=sm[fa];
for(int v:g[u]) if(v^fa) dfs2(v,u);
}
signed main(){
cin.tie(0)->ios::sync_with_stdio(false);
cout.tie(0);
for(int i = 1;i<N;++i) fac[i]=fac[i-1]*i%mod;
inv[N-1]=758835755;
for(int i = N-2;~i;--i) inv[i]=inv[i+1]*(i+1)%mod;
// freop();
//↑以上为初始化↑
//------------------------------
cin >> n;
for(int i = 1,u,v;i<n;++i)
cin >> u >> v,
g[u].push_back(v),
g[v].push_back(u);
dfs1(1,0);
for(int u:g[1]) dfs2(u,1);
for(int i = 1;i<=n;++i)
cout << dp[i] << endl;
return 0;
}

浙公网安备 33010602011771号