一、题目:
二、思路:
考虑树形 DP。
DP 状态为 (f[x,k]) 表示在 (x) 这棵子树中,(x) 的拓扑序的位置是 (k) 的方案数。
那么设接下来考虑的儿子是 (y)。(tmpf[k]) 表示考虑了 (y) 之前的儿子,(x) 的拓扑序的位置是 (k) 的方案数。
假设当前我们要用 (tmpf[p_1]) 和 (f[y,p_2]) 去更新 (f[x,p_3])。
那么分为两种情况。
-
(x) 的拓扑序在 (y) 之前。
-
首先来确定 (p_3) 的范围。我们会发现,(p_3) 至少是 (p_1);又因为 (y) 的子树中有 (p_2-1) 个节点既可以排在 (x) 前,也可以排在 (x) 后,所以 (p_3) 最大是 (p_1+p_2-1)。即 (p_1leq p_3leq p_1+p_2-1)。
-
接下来考虑转移。在 (p_3) 前面,有 (p_3-1) 个空位,选出 (p_1-1) 个来让 (x) 原来的那些拓扑序在 (x) 之前的子孙填;在 (p_3) 后面,有 (siz_x+siz_y-p_3) 个空位,选出 (siz_x-p_1) 个来让 (x) 原来的那些拓扑序在 (x) 之后的子孙填。
[f[x,p_3]overset{+}gets dbinom{p_3-1}{p_1-1}dbinom{siz_x+siz_y-p_3}{siz_x-p_1}tmpf[p_1] imes f[y,p_2] ]有些同学可能会问,你只决定了这些点的位置就可以确定整棵子树的拓扑序吗?答案是肯定的。试想,(x) 原来的那些子孙的在拓扑序中相对位置是已经确定下来的,(y) 的子孙也是。那么我们填完 (x) 原本的子孙后,从第一位依次向后找空位,先将 (y) 的子树中排在 (y) 之前的 (p_2-1) 个子孙填满,再将后 (siz_y-p_2) 个子孙填满。也就是说,(x) 的子孙一旦确定了位置,整棵子树的拓扑序也就相应确定了。
-
-
(x) 的拓扑序在 (y) 之后。
- (p_1+p_2leq p_3leq p_1+siz_y)。
- 转移同上。
注意到转移中只有一处出现了 (p_2),所以可以对 (f[y,p_2]) 使用前缀和优化。具体实现见代码。
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define FILEIN(s) freopen(s, "r", stdin)
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
const int MOD = 1000000007, MAXN = 1005;
int n, head[MAXN], tot;
int siz[MAXN];
long long f[MAXN][MAXN], tmpf[MAXN];
long long C[MAXN][MAXN];
struct Edge {
int y, next, w;
Edge() {}
Edge(int _y, int _next, int _w) : y(_y), next(_next), w(_w) {}
}e[MAXN << 1];
inline void connect(int x, int y, int w) {
e[++ tot] = Edge(y, head[x], w);
head[x] = tot;
}
void dfs(int x, int fa) {
siz[x] = 1;
f[x][1] = 1;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].y;
if (y == fa) continue;
dfs(y, x);
memcpy(tmpf, f[x], sizeof tmpf);
mem(f[x], 0);
if (e[i].w == 0) {
for (int p1 = 1; p1 <= siz[x]; ++ p1) {
for (int p3 = p1; p3 <= p1 + siz[y] - 1; ++ p3) {
(f[x][p3] += C[p3 - 1][p1 - 1] * C[siz[x] + siz[y] - p3][siz[x] - p1] % MOD * tmpf[p1] % MOD * (f[y][siz[y]] - f[y][p3 - p1]) % MOD) %= MOD;
if (f[x][p3] < 0) f[x][p3] += MOD;
}
}
}
else {
for (int p1 = 1; p1 <= siz[x]; ++ p1) {
for (int p3 = p1 + 1; p3 <= p1 + siz[y]; ++ p3) {
(f[x][p3] += C[p3 - 1][p1 - 1] * C[siz[x] + siz[y] - p3][siz[x] - p1] % MOD * tmpf[p1] % MOD * f[y][p3 - p1] % MOD) %= MOD;
}
}
}
siz[x] += siz[y];
}
for (int p3 = 2; p3 <= siz[x]; ++ p3) (f[x][p3] += f[x][p3 - 1]) %= MOD;
}
inline void prework(void) {
C[0][0] = 1;
for (int i = 1; i <= 1000; ++ i) {
C[i][0] = 1;
for (int j = 1; j <= i; ++ j)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
}
}
int main() {
prework();
int T = read();
while (T --) {
tot = 0; mem(head, 0); mem(f, 0); mem(siz, 0);
n = read();
for (int i = 1; i < n; ++ i) {
int x = read() + 1; char op[3]; scanf("%s", op); int y = read() + 1;
connect(x, y, (*op) == '>');
connect(y, x, (*op) == '<');
}
dfs(1, 0);
printf("%lld
", f[1][siz[1]]);
}
return 0;
}