Distributing Integers 树的拓扑序计数
https://vjudge.net/problem/AtCoder-abc160_f
题意:
求以每个点为根节点的树的拓扑序计数。
思路:
是一个挺经典的问题。
我们考虑自下而上的树形dp:假设我们当前点在u,我们已经求出来了\(dp[v],v∈sons_u\)。
现在要求u的方案数,我们考虑在u放上1,然后剩下\(sz[u]−1\)个数生成一个排列依次给每个结点赋值。之后类似于一个可重集的排列问题,对于每个结点,我们要除以\(sz[v]!\),就相当于我们从\(sz[u]−1\)个数中拿出来一些数给\(v\)这颗子树赋值,不考虑顺序的情况下,这些数不同的方案数。接下来就考虑上顺序,乘以一个\(dp[v]\)就行。所以总的方案数为:
\[(sz[u]−1)!⋅∏ \frac {dp[i]}{sz[i]!},u∈father(i)
\]
当然,还有更加简单的公式,我们不用考虑dp,因为我们发现首位固定最小的排列数为\((n−1)!\)。那么对于以u为根节点的子树答案为:
\[sz[u]!∏ \frac 1 {sz[i]},u∈father(i)
\]
这两个式子是等价的,我们可以直接归纳证明。
这样就求出来了以一个点作为根节点的情况,求所有点作为根结点的情况直接换根跑一下就行。
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
int head[maxn], to[maxn << 1], nxt[maxn << 1], ecnt = 0;
inline void add(int u, int v) {
to[++ecnt] = v; nxt[ecnt] = head[u]; head[u] = ecnt;
to[++ecnt] = u; nxt[ecnt] = head[v]; head[v] = ecnt;
}
int fact[maxn], finv[maxn], inv[maxn];
void init() {
fact[0] = fact[1] = finv[0] = finv[1] = inv[1] = 1;
for(int i = 2; i < maxn; ++ i) {
fact[i] = fact[i - 1] * i % mod;
inv[i] = (mod - mod / i) * inv[mod % i] % mod;
finv[i] = finv[i - 1] * inv[i] % mod;
}
}
int ans[maxn], sz[maxn];
int dfs(int now, int fa) {
sz[now] = 1;
for(int i = head[now]; i; i = nxt[i]) {
if(to[i] == fa) continue;
sz[now] += dfs(to[i], now);
}
return sz[now];
}
int n;
void dfs2(int now, int fa) {
for(int i = head[now]; i; i = nxt[i]) {
if(to[i] == fa) continue;
ans[to[i]] = ans[now] * sz[to[i]] % mod * inv[n - sz[to[i]]] % mod;
dfs2(to[i], now);
}
}
void run() {
for(int i = 1; i <= n; ++ i) head[i] = 0; ecnt = 0;
for(int i = 1; i < n; ++ i) {
int u = rd(), v = rd();
add(u, v);
}
dfs(1, 1);
ans[1] = fact[n];
for(int i = 1; i <= n; ++ i) ans[1] = ans[1] * inv[sz[i]] % mod;
dfs2(1, 1);
for(int i = 1; i <= n; ++ i) printf("%lld\n", ans[i]);
return ;
}

浙公网安备 33010602011771号