Address
Solution
定义状态:
f
[
u
]
[
i
]
f[u][i]
f[u][i] 表示
u
u
u 的子树内所有的点进行排列,
u
u
u 排在位置
i
i
i 的方案数。
考虑按照树形背包的方式转移,设
f
′
[
u
]
f'[u]
f′[u] 为枚举到
u
u
u 的子节点
v
v
v 之前的 DP 数组。
如何合并
f
′
[
u
]
f'[u]
f′[u] 和
f
[
v
]
f[v]
f[v] 呢?
先考虑
u
u
u 必须排在
v
v
v 后面的情况:
先枚举
i
i
i 和
j
j
j ,考虑如何从
f
′
[
u
]
[
i
]
f'[u][i]
f′[u][i] 和
f
[
v
]
[
]
f[v][]
f[v][] 合并到
f
[
u
]
[
i
+
j
]
f[u][i+j]
f[u][i+j] 。
(上面的
i
i
i 表示枚举到
u
u
u 的子节点
v
v
v 之前子树中
u
u
u 的排名,
j
j
j 表示
v
v
v 的子树内排名在
u
u
u 前面的点数)。
而如果
u
u
u 必须排在
v
v
v 的后面,这就要求了能参与转移的
f
[
v
]
[
k
]
f[v][k]
f[v][k] 必须满足
k
≤
j
k\le j
k≤j 。
设
u
u
u 的子节点
v
v
v 之前子树大小为
s
u
′
s'_u
su′ ,
v
v
v 的子树大小为
s
v
s_v
sv ,如何求把
f
′
[
u
]
[
i
]
f'[u][i]
f′[u][i] 和
f
[
v
]
[
k
]
f[v][k]
f[v][k] (
k
≤
j
k\le j
k≤j )合并起来的方案数呢?
这等价于把两个长度分别为
s
u
′
s'_u
su′ 和
s
v
s_v
sv 的序列合并成一个序列,使得新序列任意两个相同元素在原序列中的相对位置不变,并且对于
x
x
x (
x
x
x 为新序列中第
i
i
i 个来自序列
s
u
′
s'_u
su′ 的元素),必须满足
1
1
1 到
x
x
x 中恰好有
j
j
j 个元素来自序列
s
v
s_v
sv 。
这又等价于把
s
v
s_v
sv 个元素切割成
s
u
′
+
1
s'_u+1
su′+1 块(块内可以为空),满足前
i
i
i 块里恰好有
j
j
j 个元素。
根据组合数学的知识得到这样的方案数为:
C
i
+
j
−
1
i
−
1
×
C
s
u
′
−
i
+
s
v
−
j
s
u
′
−
i
C_{i+j-1}^{i-1}\times C_{s'_u-i+s_v-j}^{s'_u-i}
Ci+j−1i−1×Csu′−i+sv−jsu′−i
所以转移:
f
[
u
]
[
i
+
j
]
+
=
C
i
+
j
−
1
i
−
1
×
C
s
u
′
−
i
+
s
v
−
j
s
u
′
−
i
×
f
′
[
u
]
[
i
]
×
∑
k
≤
j
f
[
v
]
[
k
]
f[u][i+j]+=C_{i+j-1}^{i-1}\times C_{s'_u-i+s_v-j}^{s'_u-i}\times f'[u][i]\times \sum_{k\le j}f[v][k]
f[u][i+j]+=Ci+j−1i−1×Csu′−i+sv−jsu′−i×f′[u][i]×k≤j∑f[v][k]
u
u
u 排在
v
v
v 之前时:
f
[
u
]
[
i
+
j
]
+
=
C
i
+
j
−
1
i
−
1
×
C
s
u
′
−
i
+
s
v
−
j
s
u
′
−
i
×
f
′
[
u
]
[
i
]
×
∑
k
≥
j
f
[
v
]
[
k
]
f[u][i+j]+=C_{i+j-1}^{i-1}\times C_{s'_u-i+s_v-j}^{s'_u-i}\times f'[u][i]\times \sum_{k\ge j}f[v][k]
f[u][i+j]+=Ci+j−1i−1×Csu′−i+sv−jsu′−i×f′[u][i]×k≥j∑f[v][k]
∑
k
≤
j
\sum_{k\le j}
∑k≤j 和
∑
k
≥
j
\sum_{k\ge j}
∑k≥j 可以对
f
f
f 求前缀和得出。
由于
f
[
u
]
f[u]
f[u] 的第二维的上界只有
s
u
s_u
su ,所以复杂度相当于每对点都在 LCA 处贡献了
O
(
1
)
O(1)
O(1) ,所以复杂度
O
(
T
n
2
)
O(Tn^2)
O(Tn2) 。
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Tree(u) for (int e = adj[u], v; e; e = nxt[e]) if ((v = go[e]) != fu)
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
inline char get()
{
char c;
while ((c = getchar()) != '<' && c != '>');
return c;
}
const int N = 3005, M = N << 1, ZZQ = 1e9 + 7;
int n, ecnt, nxt[M], adj[N], go[M], sze[N], f[N][N], C[M][M],
x[N], s[N][N], ans;
bool cm[M];
void add_edge(int u, int v, bool op)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; cm[ecnt] = op;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; cm[ecnt] = op ^ 1;
}
int orz(int n, int m, int x, int y)
{
return 1ll * C[x + y - 1][x - 1] * C[n - x + m - y][n - x] % ZZQ;
}
void dfs(int u, int fu)
{
int i, j;
sze[u] = f[u][1] = 1;
Tree(u)
{
dfs(v, u);
For (i, 0, sze[u] + sze[v]) x[i] = 0;
For (i, 1, sze[u]) For (j, 0, sze[v])
x[i + j] = (1ll * f[u][i] * (cm[e] ? s[v][sze[v]] -
s[v][j] + ZZQ : s[v][j]) % ZZQ
* orz(sze[u], sze[v], i, j) + x[i + j]) % ZZQ;
For (i, 1, sze[u] + sze[v]) f[u][i] = x[i];
sze[u] += sze[v];
}
For (i, 1, sze[u]) s[u][i] = (s[u][i - 1] + f[u][i]) % ZZQ;
}
void work()
{
int i, x, y; char op;
ecnt = ans = 0;
n = read();
For (i, 1, n) adj[i] = 0;
For (i, 1, n - 1)
x = read() + 1, op = get(), y = read() + 1,
add_edge(x, y, op == '<' ? 1 : 0);
dfs(1, 0);
For (i, 1, n) ans = (ans + f[1][i]) % ZZQ;
printf("%d\n", ans);
}
int main()
{
int i, j, T = read();
For (i, 0, 1000) C[i][0] = 1;
For (i, 1, 1000) For (j, 1, i)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % ZZQ;
while (T--) work();
return 0;
}