P4099 [HEOI2013]SAO
题意:一个有向树的拓扑序个数,对大质数取模。
\(n \leq 10^3\).
做法:联想到树形背包,因为 \(n\leq 10^3\). 然后随即想到去直接规划拓扑序。我觉得树的性质一定是有用的,因为 DAG 上做这个看起来就很困难。但是想了半天还是不知道怎么转移。
有一个初级的想法:设 \(f_u\) 为 \(u\) 子树的拓扑序方案数。但是结合数据范围和实际转移都可以发现很难转移。为什么呢?
我们发现两个子树的拓扑序实际上是这两个子树的拓扑序 mix 到了一起,同时需要保证 mix 后 \(v\) 在 \(u\) 的前面 / 后面。我们只需要知道这个 mix 的方案数即可。
想要知道这个信息,我们发现只需要记录\(u\) 在子树拓扑序内的排名即可。所以改一下转移,令 \(f_{u, i}\) 表示 \(u\) 子树内,\(u\) 当前位于拓扑序第 \(i\) 位的方案数。转移具体需要分两类讨论,不过是殊途同归的。
然后写出来暴力,发现 \(ik\) 两维的总和是 \(O(n^2)\) 的,\(j\) 不在式子里,就可以使用前缀和优化到 \(O(n^2)\) 了。
#include <bits/stdc++.h>
#define rep(i,l,r) for(int i = (l); i <= (r); ++i)
#define per(i,r,l) for(int i = (r); i >= (l); --i)
using namespace std;
typedef long long ll;
inline int gi() {
int f = 1, x = 0; char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -f;ch = getchar();}
while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
return f * x;
}
const int N = 1e3 + 5, P = 1e9 + 7;
int T, n;
vector<pair<int, int> > g[N];
// (u, v, 1): v finish before u
// (u, v, 0): v finish after u
int dp[N][N], sz[N], sum[N];
int tmp[N], c[N][N];
inline int add (int a, int b) {
return ((a += b) >= P ? a - P : a);
}
void mg (int u, int v, int w) {
memset (tmp, 0, sizeof (tmp));
rep (i, 1, n) sum[i] = add(sum[i - 1], dp[v][i]);
if (w == 0) {
rep (i, 1, sz[u])
rep(k, i, i + sz[v]){
// k <= i + j - 1
// j >= k - i + 1
int f = (ll)c[sz[u] + sz[v] - k][sz[u] - i]
* c[k - 1][i - 1] % P;
tmp[k] = add(tmp[k], (ll)f * dp[u][i] % P * (sum[n] - sum[k - i] + P) % P);
}
} else {
rep (i, 1, sz[u])
rep(k, i, i + sz[v]){
// k >= i + j
// j <= k - i
int f = (ll)c[k - 1][i - 1] *
c[sz[u] + sz[v] - k][sz[u] - i] % P;
tmp[k] = add(tmp[k], (ll)f * dp[u][i] % P * sum[k - i] % P);
}
}
memcpy (dp[u], tmp, sizeof (tmp));
}
void dfs (int u, int p = 0) {
memset (dp[u], 0, sizeof (dp[u]));
dp[u][1] = 1;
sz[u] = 1;
for (int i = 0; i < g[u].size(); ++i) {
int v = g[u][i].first, w = g[u][i].second;
if (v == p) continue;
dfs (v, u);
mg (u, v, w);
sz[u] += sz[v];
}
}
void slv() {
n = gi();
c[0][0] = 1;
rep (i, 1, n) g[i].clear();
rep (i, 1, n) {
c[i][0] = c[i][i] = 1;
rep (j, 1, i - 1)
c[i][j] = add(c[i - 1][j], c[i - 1][j - 1]);
}
rep (i, 1, n - 1) {
int u = gi() + 1;
string t;
cin >> t;
int v = gi() + 1;
g[u].push_back({v, t[0] == '>'});
g[v].push_back({u, t[0] == '<'});
}
dfs (1);
int ans = 0;
rep (i, 1, n) ans = add(ans, dp[1][i]);
cout << ans << '\n';
}
int main() {
for (int T = gi(); T; T--)
slv();
return 0;
}

浙公网安备 33010602011771号