Description:
一棵树,每条边上有一个不等号,求每个点填一个排列的数量。
Solution:
dp[u][i]
d
p
[
u
]
[
i
]
表示第
u
u
个点在子树中排名为的方案数,那么枚举新加入的点有多少个比
u
u
小。
对于的情况,有
dp[u][j+k]+=dp[u][j]∗C(k+j−1,k)∗C(sz[u]+sz[v]−j−k,sz[v]−k)∗∑ki=1dp[v][i]
d
p
[
u
]
[
j
+
k
]
+
=
d
p
[
u
]
[
j
]
∗
C
(
k
+
j
−
1
,
k
)
∗
C
(
s
z
[
u
]
+
s
z
[
v
]
−
j
−
k
,
s
z
[
v
]
−
k
)
∗
∑
i
=
1
k
d
p
[
v
]
[
i
]
枚举了
v
v
中有多少个比小,乘上对应
v
v
所有可能的排名情况,然后把比
u
u
小的那部分和之前比小的做归并,即
C(k+j−1,k)
C
(
k
+
j
−
1
,
k
)
,后面则是后面一部分做归并。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1005, P = 1e9 + 7;
struct edge {
int nxt, to, w;
} e[maxn * 2];
int n, cnt = 1;
int h[maxn], sz[maxn];
long long dp[maxn][maxn], sum[maxn][maxn], c[maxn][maxn], tmp[maxn];
void link(int u, int v, int w) {
e[++cnt].nxt = h[u];
h[u] = cnt;
e[cnt].to = v;
e[cnt].w = w;
}
void dfs(int u, int f) {
sz[u] = 1;
dp[u][1] = 1;
sum[u][1] = 1;
for(int i = h[u]; i; i = e[i].nxt) {
int v = e[i].to;
if(v != f) {
dfs(v, u);
memset(tmp, 0, sizeof(tmp));
for(int j = sz[u]; ~j; --j) {
for(int k = sz[v]; ~k; --k) {
tmp[j + k] = (tmp[j + k] + dp[u][j] * (e[i].w > 0 ? sum[v][k] : (sum[v][sz[v]] - sum[v][k] + P) % P) % P * c[k + j - 1][k] % P * c[sz[u] + sz[v] - j - k][sz[v] - k] % P) % P;
}
}
sz[u] += sz[v];
for(int j = 1; j <= sz[u]; ++j) {
dp[u][j] = tmp[j];
}
}
}
for(int i = 1; i <= sz[u]; ++i) {
sum[u][i] = (sum[u][i - 1] + dp[u][i]) % P;
}
}
int main() {
int T;
scanf("%d", &T);
c[0][0] = 1;
for(int i = 1; i < maxn; ++i) {
c[i][0] = 1;
for(int j = 1; j <= i; ++j) {
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P;
}
}
while(T--) {
cnt = 1;
memset(h, 0, sizeof(h));
memset(dp, 0, sizeof(dp));
memset(sum, 0, sizeof(sum));
scanf("%d", &n);
for(int i = 1; i < n; ++i) {
int u, v, w;
char C[10];
scanf("%d%s%d", &u, C, &v);
++u;
++v;
if(C[0] == '>') {
w = 1;
} else {
w = -1;
}
link(u, v, w);
link(v, u, -w);
}
dfs(1, 0);
printf("%lld\n", sum[1][n]);
}
return 0;
}