Address
Solution
定义状态:
表示
的子树内所有的点进行排列,
排在位置
的方案数。
考虑按照树形背包的方式转移,设
为枚举到
的子节点
之前的 DP 数组。
如何合并
和
呢?
先考虑
必须排在
后面的情况:
先枚举
和
,考虑如何从
和
合并到
。
(上面的
表示枚举到
的子节点
之前子树中
的排名,
表示
的子树内排名在
前面的点数)。
而如果
必须排在
的后面,这就要求了能参与转移的
必须满足
。
设
的子节点
之前子树大小为
,
的子树大小为
,如何求把
和
(
)合并起来的方案数呢?
这等价于把两个长度分别为
和
的序列合并成一个序列,使得新序列任意两个相同元素在原序列中的相对位置不变,并且对于
(
为新序列中第
个来自序列
的元素),必须满足
到
中恰好有
个元素来自序列
。
这又等价于把
个元素切割成
块(块内可以为空),满足前
块里恰好有
个元素。
根据组合数学的知识得到这样的方案数为:
所以转移:
排在
之前时:
和
可以对
求前缀和得出。
由于
的第二维的上界只有
,所以复杂度相当于每对点都在 LCA 处贡献了
,所以复杂度
。
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;
}