這道題是個不錯的計數題,考察了調換求和順序再前綴和優化,難點在狀態設計,比較考察思惟。
一句話題意:給你一棵數,樹邊爲有向邊,求其拓撲序數。
對DAG求拓撲數是一個NP問題,可是這裏保證是一棵樹,因此咱們能夠用樹形DP來求解。
狀態的設計上,光設結點編號\(u\)不夠,還須要設計一維\(i\)表示結點\(u\)在以\(u\)爲根的子樹中的拓撲序的第\(i\)位,這樣咱們就能夠寫轉移方程了。
對於\(u \rightarrow v\)
\[ F'[u][k] = \Sigma_{v\in son} F[u][i]\times F[v][j] \times \tbinom{k-1}{i-1} \times \tbinom{size_u+size_v-k}{size_u-i},i \leq k \leq i+j-1 \]
對於\(u \leftarrow v\)
\[ F'[u][k] = \Sigma_{v\in son} F[u][i]\times F[v][j] \times \tbinom{k-1}{i-1} \times \tbinom{size_u+size_v-k}{size_u-i},i+j \leq k \leq size_v+i \]
目標:
\[ \Sigma_{i=1}^{N} F[1][i] \]
(全部結點的下標自動+1)
發現三重循環鐵定不行,調換下\(j\)和\(k\)的順序,發現\(F[v][j]\)能夠前綴和處理,削掉一維。
事實上這樣作後總體的複雜度由\(\text{O}(N^3)\)降爲\(\text{O}(N^2)\)。(仔細研讀下面的代碼發現實際上處理次數爲點對數)
細節看碼。c++
#include <bits/stdc++.h> using namespace std; #define ll long long #define INF (1 << 30) #define chkmax(a, b) a = max(a, b) #define chkmin(a, b) a = min(a, b); inline int read() { int w = 0, f = 1; char c; while (!isdigit(c = getchar())) f = c == '-' ? -1 : f; while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ 48), c = getchar(); return w * f; } inline int read_ch() { char c; while (c = getchar(), c != '>' && c != '<'); return c == '<'; } const int maxn = 1000 + 5; const int MOD = 1e9 + 7; struct Edge { int v, w, pre; } e[maxn << 1]; int m, G[maxn]; void clear() { m = 0; memset(G, -1, sizeof(G)); } void add(int u, int v, int w) { e[m++] = (Edge){v, w, G[u]}; G[u] = m-1; } int T, N; int f[maxn][maxn], g[maxn], C[maxn][maxn]; void inc(int &a, int b) { a += b; if (a >= MOD) a -= MOD; } int dec(int a) { if (a < 0) a += MOD; return a; } void init() { C[0][0] = 1; for (register int i = 1; i <= 1000; i++) { C[i][0] = 1; for (register int j = 1; j <= i; j++) C[i][j] = (C[i-1][j] + C[i-1][j-1]) % MOD; } } int size[maxn]; void dfs(int u, int fa) { size[u] = 1; f[u][1] = 1; for (register int i = G[u]; ~i; i = e[i].pre) { \\ 這裏實際上至關於u<->v之間的拓撲序合併起來 int v = e[i].v; if (v == fa) continue; dfs(v, u); memcpy(g, f[u], sizeof(g)); memset(f[u], 0, sizeof(f[u])); if (e[i].w) { for (register int i = 1; i <= size[u]; i++) for (register int k = i; k <= i+size[v]-1; k++) inc(f[u][k], (ll)g[i] * dec(f[v][size[v]]-f[v][k-i]) % MOD * C[k-1][i-1] % MOD * C[size[u]+size[v]-k][size[u]-i] % MOD); } else { for (register int i = 1; i <= size[u]; i++) for (register int k = i+1; k <= size[v] + i; k++) inc(f[u][k], (ll)g[i] * dec(f[v][k-i]) % MOD * C[k-1][i-1] % MOD * C[size[u]+size[v]-k][size[u]-i] % MOD); } size[u] += size[v]; } for (register int i = 1; i <= size[u]; i++) inc(f[u][i], f[u][i-1]); } int main() { init(); T = read(); while (T--) { N = read(); clear(); for (register int i = 1; i < N; i++) { int u = read()+1, opt = read_ch(), v = read()+1; add(u, v, opt); add(v, u, !opt); } dfs(1, 1); printf("%d\n", f[1][N]); } return 0; }