Description给定一颗 n n n 个点的有根树,1 1 1 是根,记 u u u 的父亲是 f a u fa_u f a u 。另给出一长度为 n n n 的权值序列 b b b 。
称一个长度为 n n n 的排列 a a a 为这颗树的合法拓扑序,当且仅当 ∀ 2 ≤ u ≤ n , a u > a f a u \forall 2 \le u \le n,a_u > a_{fa_u} ∀ 2 ≤ u ≤ n , a u > a f a u 。
对每个点 u u u ,定义 f ( u ) f(u) f ( u ) 为,在所有这颗树的合法拓扑序中,b a u b_{a_u} b a u 之和。
现在对 1 ≤ u ≤ n 1 \le u \le n 1 ≤ u ≤ n ,求 f ( u ) m o d 1 0 9 + 7 f(u) \bmod 10^9+7 f ( u ) m o d 1 0 9 + 7 。
2 ≤ n ≤ 5000 2 \le n \le 5000 2 ≤ n ≤ 5 0 0 0 ,1 ≤ f a i < i 1 \le fa_i < i 1 ≤ f a i < i ,0 ≤ b i < 1 0 9 + 7 0 \le b_i < 10^9+7 0 ≤ b i < 1 0 9 + 7 。
Solution先考虑对于每个 x x x 怎么求出答案。
假设当前处理到了节点 u u u (要求 u u u 是 x x x 的祖先),v v v 为 u u u 的儿子中子树包含 x x x 的儿子。
设 f u f_u f u 表示 u u u 的子树的合法拓扑序数量,g u , i g_{u,i} g u , i 表示 u u u 的子树里已经钦定恰好有 i i i 个点 dfs 序小于 x x x 的方案数,h u h_u h u 表示 u u u 的子树去掉包含 x x x 的子树剩下的点的合法拓扑序数。可以得到转移:
f u = ( s z u − 1 ) ! ∏ w ∈ son u f w / ( s z w ! ) h u = ∏ w ∈ son u , w ≠ v f w / ( s z w ! ) g u , i + j + 1 = ∑ 0 ≤ i ≤ s z v − 1 , 0 ≤ j ≤ s z u − s z v − 1 ( i + j j ) ( s z u − i − j − 2 s z v − i − 1 ) h u g v , i \begin{aligned} f_u&=(sz_u-1)!\prod_{w\in \text{son}_u}{f_w/(sz_w!)}\\ h_u&=\prod_{w\in \text{son}_u,w\neq v}{f_w/(sz_w!)}\\ g_{u,i+j+1}&=\sum_{0\leq i\leq sz_v-1,0\leq j\leq sz_u-sz_v-1}{\binom{i+j}{j}\binom{sz_u-i-j-2}{sz_v-i-1}h_ug_{v,i}} \end{aligned} f u h u g u , i + j + 1 = ( s z u − 1 ) ! w ∈ son u ∏ f w / ( s z w ! ) = w ∈ son u , w = v ∏ f w / ( s z w ! ) = 0 ≤ i ≤ s z v − 1 , 0 ≤ j ≤ s z u − s z v − 1 ∑ ( j i + j ) ( s z v − i − 1 s z u − i − j − 2 ) h u g v , i
最终的答案即为 ∑ i = 1 n g 1 , i − 1 b i \sum_{i=1}^{n}{g_{1,i-1}b_i} ∑ i = 1 n g 1 , i − 1 b i 。
时间复杂度:O ( n 3 ) O(n^3) O ( n 3 ) 。
考虑怎么优化。
容易发现这题主要慢在每次要处理从一个点到根的路径上的 dp,而不是从根到某个点的路径,这样会导致每个点之间的信息没有任何交集。
注意到这题转移的终点是一定的,就是根,而起点不一样。所以可以类似这题 的思路把转移倒过来做。
具体的,将 g u , i g_{u,i} g u , i 的状态改为当前状态为 ( u , i ) (u,i) ( u , i ) ,到最终状态对答案的贡献。转移改为:
g v , i = ∑ 0 ≤ i ≤ s z v − 1 , 0 ≤ j ≤ s z u − s z v − 1 ( i + j j ) ( s z u − i − j − 2 s z v − i − 1 ) h u g u , i + j + 1 g_{v,i}=\sum_{0\leq i\leq sz_v-1,0\leq j\leq sz_u-sz_v-1}{\binom{i+j}{j}\binom{sz_u-i-j-2}{sz_v-i-1}h_ug_{u,i+j+1}} g v , i = 0 ≤ i ≤ s z v − 1 , 0 ≤ j ≤ s z u − s z v − 1 ∑ ( j i + j ) ( s z v − i − 1 s z u − i − j − 2 ) h u g u , i + j + 1
最终 x x x 的答案就是 f x g x , 0 f_xg_{x,0} f x g x , 0 。
时间复杂度:O ( n 2 ) O(n^2) O ( n 2 ) 。
Code1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 #include <bits/stdc++.h> #define int int64_t const int kMaxN = 5e3 + 5 , kMod = 1e9 + 7 ;int n;int a[kMaxN], C[kMaxN][kMaxN], sz[kMaxN], f[kMaxN], g[kMaxN][kMaxN]; std::vector<int > G[kMaxN];constexpr int qpow (int bs, int64_t idx = kMod - 2 ) { int ret = 1 ; for (; idx; idx >>= 1 , bs = (int64_t )bs * bs % kMod) if (idx & 1 ) ret = (int64_t )ret * bs % kMod; return ret; }inline int add (int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }inline int sub (int x, int y) { return (x >= y ? x - y : x - y + kMod); }inline void inc (int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }inline void dec (int &x, int y) { (x -= y) < 0 ? x += kMod : x; }void prework () { C[0 ][0 ] = 1 ; for (int i = 1 ; i <= n; ++i) { C[i][0 ] = 1 ; for (int j = 1 ; j <= i; ++j) C[i][j] = add (C[i - 1 ][j], C[i - 1 ][j - 1 ]); } }void dfs1 (int u) { f[u] = 1 , sz[u] = 0 ; for (auto v : G[u]) { dfs1 (v); f[u] = 1ll * f[u] * f[v] % kMod * C[sz[u] += sz[v]][sz[v]] % kMod; } ++sz[u]; }void dfs2 (int u) { for (auto v : G[u]) { int coef = 1 , now = 0 ; for (auto w : G[u]) { if (w != v) coef = 1ll * coef * f[w] % kMod * C[now += sz[w]][sz[w]] % kMod; } for (int i = 0 ; i <= sz[v] - 1 ; ++i) for (int j = 0 ; j <= sz[u] - sz[v] - 1 ; ++j) inc (g[v][i], 1ll * coef * C[i + j][j] % kMod * C[sz[u] - i - j - 2 ][sz[v] - i - 1 ] % kMod * g[u][i + j + 1 ] % kMod); dfs2 (v); } }void dickdreamer () { std::cin >> n; for (int i = 2 ; i <= n; ++i) { int p; std::cin >> p; G[p].emplace_back (i); } for (int i = 1 ; i <= n; ++i) std::cin >> a[i]; prework (); for (int i = 1 ; i <= n; ++i) g[1 ][i - 1 ] = a[i]; dfs1 (1 ), dfs2 (1 ); for (int i = 1 ; i <= n; ++i) std::cout << 1ll * f[i] * g[i][0 ] % kMod << ' ' ; }int32_t main () {#ifdef ORZXKR freopen ("in.txt" , "r" , stdin); freopen ("out.txt" , "w" , stdout);#endif std::ios::sync_with_stdio (0 ), std::cin.tie (0 ), std::cout.tie (0 ); int T = 1 ; while (T--) dickdreamer (); return 0 ; }