[dp记录] CF512D Fox And Travelling
第一感觉是先把环上的点去掉,然后对剩下的森林做 \(dp\),但是这样是有问题的:
\(\bullet\) 两个环之间的路径上的点也是无法选择的;
\(\bullet\) 剩下的森林中,有些树挂在某些环上,属于有根树,有些树是独立的无根树。
对于第一个问题,可以考虑模拟选点,每次找到度数 \(\le 1\) 的点删去,最后剩下没删的点就是无法选择的点;
对于第二个问题,分类计算:
\(\bullet\) 有根树:记 \(dp(u,i)\) 表示子树 \(u\) 中选择 \(i\) 个点的方案数,直接背包合并儿子即可;
\(\bullet\) 无根树:无根树比较麻烦,因为选定的根可能由于删到只剩一棵子树,它本身也变成了叶子,比较棘手;
套路的,我们考虑钦定一个数 \(i\) 表示 \(i\) 是删剩下的点集里面编号最小的点,这样数是不重不漏的,然后 \(dp\) 时需要强制编号 \(< i\) 的点都删去。
但是这样算不了全部删去的方案数,其实也是差不多的做法:枚举最后一个删去的点 \(i\),以 \(i\) 为根做一遍有根树的 \(dp\) 即可。
最后将所有的树背包合并即为答案。
时间复杂度 \(O(n^3)\)。
\(Ps:\) 题解区还有 \(EGF\) 做法可以做到 \(O(n^2)\)。
\(\texttt{Code}\)
#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
using namespace std;
const int cmd = 1e9 + 9;
const int N = 105;
const int M = 1e4 + 5;
int n, m, tag[N], C[N][N], dp[N][N], vis[N], f[N], rt[N], F[N], deg[N], s[N];
pair<int, int> e[M];
vector<int> g[N], G[N], vec;
void topo() {
queue<int> q;
for (int i = 1; i <= n; i++)
if (deg[i] <= 1) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
tag[u] = 1;
for (int v : g[u])
if (--deg[v] == 1) q.push(v);
}
}
void dfs(int u, int c) {
vis[u] = 1;
if (!c && tag[u]) c = u;
rt[u] = c;
for (int v : g[u])
if (!vis[v]) dfs(v, c);
}
void getp(int u, int fa) {
vec.pb(u); vis[u] = 1;
for (int v : G[u]) if (v != fa) getp(v, u);
}
void calc(int u, int fa, int rt) {
dp[u][0] = 1; s[u] = 0;
for (int v : G[u]) {
if (v == fa) continue;
calc(v, u, rt);
for (int i = 0; i <= s[u] + s[v]; i++) f[i] = 0;
for (int i = 0; i <= s[u]; i++)
for (int j = 0; j <= s[v]; j++)
f[i + j] = (f[i + j] + 1ll * dp[u][i] * dp[v][j] % cmd * C[i + j][i]) % cmd;
for (int i = 0; i <= s[u] + s[v]; i++) dp[u][i] = f[i];
s[u] += s[v];
}
s[u]++;
dp[u][s[u]] = dp[u][s[u] - 1];
if (u < rt) for (int i = 0; i < s[u]; i++) dp[u][i] = 0;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i <= n; i++) {
C[i][0] = 1;
for (int j = 1; j <= i; j++)
C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % cmd;
}
for (int i = 1; i <= m; i++) {
scanf("%d%d", &e[i].fi, &e[i].se);
g[e[i].fi].pb(e[i].se);
g[e[i].se].pb(e[i].fi);
deg[e[i].fi]++;
deg[e[i].se]++;
}
topo();
for (int i = 1; i <= m; i++)
if (tag[e[i].fi] && tag[e[i].se]) {
G[e[i].fi].pb(e[i].se);
G[e[i].se].pb(e[i].fi);
}
for (int i = 1; i <= n; i++)
if (!tag[i] && !vis[i]) dfs(i, 0);
int cur = 0;
dp[0][0] = 1;
memset(vis, 0, sizeof(vis));
for (int i = 1; i <= n; i++)
if (tag[i] && !vis[i]) {
vec.clear();
getp(i, 0);
sort(vec.begin(), vec.end());
for (int j = 0; j <= (int)vec.size(); j++) F[j] = 0;
if (rt[i]) {
calc(rt[i], 0, 0);
for (int j = 0; j <= (int)vec.size(); j++) F[j] = dp[rt[i]][j];
} else {
for (int u : vec) {
calc(u, 0, u);
for (int j = 0; j < (int)vec.size(); j++) F[j] = (F[j] + dp[u][j]) % cmd;
}
for (int u : vec) {
calc(u, 0, 0);
F[vec.size()] = (F[vec.size()] + dp[u][vec.size()]) % cmd;
}
}
for (int j = 0; j <= cur + (int)vec.size(); j++) f[j] = 0;
for (int j = 0; j <= cur; j++)
for (int k = 0; k <= (int)vec.size(); k++)
f[j + k] = (f[j + k] + 1ll * dp[0][j] * F[k] % cmd * C[j + k][j]) % cmd;
for (int j = 0; j <= cur + (int)vec.size(); j++) dp[0][j] = f[j];
cur += (int)vec.size();
}
for (int i = 0; i <= n; i++) printf("%d\n", dp[0][i]);
return 0;
}

浙公网安备 33010602011771号