• 定义:一棵有根树形成的任一排列 $p$,若 $i$ 是 $j$ 的父亲,排列 $p$ 均满足 $i$ 在 $j$ 之前。更加形式化的,$\forall 1 \le i < j \le n$,$p_j$ 均不是 $p_i$ 的父亲。

  • 结论:设 $f_u$ 表示以 $u$ 为根时该子树的合法拓扑序的数量,$sz_u$ 表示子树大小,则有 $f_u = \dfrac{sz_u!}{\prod sz_v \mid v \in \rm{subtree}(u)}$。

以下是证明:

首先考虑二叉树的情况。若以 $u$ 为根,此时每颗子树内的相对顺序是确定的,只需要考虑子树间的相对顺序。故有:

若是三叉树,考虑将 $v_1$ 与 $v_2$ 的相对顺序先固定形成一个整体后,再考虑 $v_3$,则可得:

推广到一般,则可推出:

再由 $(sz_u - 1)! = \frac{sz_u!}{sz_u}$ 做进一步的化简:

继续迭代可得:

若以 $1$ 为根答案为 $f_1 = \dfrac{n!}{\prod \limits _{i = 1}^n sz_i}$。而现在需要求以任意一点为根时的答案,故考虑换根 dp。当由 $f_u$ 转移至 $f_v$ 时,只有两点的 $sz$ 发生变化,故可得到转移方程 $f_v = f_u \times \frac{n - sz_v}{sz_v}$。两次 dfs 即可求得所有的 $f_i$,暴力逆元维护,时间复杂度 $O(n \log n)$。

设 $dp_{i,j}$ 表示节点 $i$ 位于第 $j$ 个位置的方案数。若 $u$ 是 $v$ 的父亲,则当 $dp_{u,i}$ 转移至 $dp_{v,j}$ 时,由于 $u$ 内存在其余子树,为了方便转移,方程中 $dp_{i,j}$ 不考虑子树 $i$ 内的顺序,在统计答案的时候再作更新。当 $dp_{u,i}$ 转移至 $dp_{v,j}$ 时,需要考虑的就是 $u$ 的其余子树在整一拓扑序中的位置,以及 $u$ 的其余子树的相对拓扑序。
前者就是用组合数求出在剩余的位置中找 $sz_u - sz_v - 1$ 个将 $u$ 内不含 $v$ 子树内的节点放入的方案数,后者直接套结论。故有:

最后的答案便是:

注意到转移方程可以前缀和优化,所以最后的时间复杂度是 $O(n^2)$。千万需要先预处理出逆元,$O(n^2 \log n)$ 的做法在 CF 上会挂。放个核心代码吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void dfs1 (int u,int fa)
{
sz[u] = prod[u] = 1;
for (auto v : ve[u])
{
if (v == fa) continue;
dfs1 (v,u);
sz[u] += sz[v];
prod[u] = 1ll * prod[u] * prod[v] % MOD;
}
prod[u] = 1ll * prod[u] * sz[u] % MOD;
}
void dfs2 (int u,int fa)
{
for (auto v : ve[u])
{
if (v == fa) continue;
int tmp = qpow (1ll * prod[u] * inv_prod[v] % MOD * inv_sz[u] % MOD,MOD - 2);
for (int j = 1;j <= n;++j) ndp[j] = (1ll * dp[u][j] * calc (n - j - sz[v],sz[u] - sz[v] - 1) % MOD + ndp[j - 1]) % MOD;
for (int j = 1;j <= n;++j) dp[v][j] = 1ll * ndp[j - 1] * f[sz[u] - sz[v] - 1] % MOD * tmp % MOD;
dfs2 (v,u);
}
}