ZJOI2017 仙人掌 轉化模型後的簡單樹形dp
阿新 • • 發佈:2019-01-30
題目大意
給定一個
多組資料。
解題思路
首先討論給定的圖是樹的情況。
(轉化一)
要求不能有邊存在與兩個簡單環內相當於我們要加入新的邊去覆蓋這個樹,是的每條邊最多被覆蓋一次。
由於每條邊不一定被覆蓋,所以比較麻煩。注意到題目的一個性質,不能存在重邊,那麼意味著我們可以將原來構出仙人掌中不存在環內的邊視為向原來樹中的父親連了一條邊,這個與原問題是等價的。這樣就有了每條樹邊都要被覆蓋一次的限制。
(轉化二)
問題就轉化成了有多少種覆蓋方案使得樹上每條邊恰好被覆蓋一次。
這就很好做了,設
轉移就很顯然了。
(分
對於原圖不是樹的情況可以先把原圖不是仙人掌的情況判掉,答案肯定是0。由於原圖的環是不能被任何邊覆蓋的,所以直接把環上的邊刪掉,轉化成若干棵樹即可。
程式
//YxuanwKeith
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 5e5 + 5, MAXM = 1e6 + 5;
const int Mo = 998244353;
struct Path {
int u, v;
} path[MAXN];
int n, m, tim, bel[MAXN], deep[MAXN], fa[MAXN], f[MAXN], g[MAXN], h[MAXN], tag[MAXN];
int tot, Last[MAXN], Next[MAXM * 2], Go[MAXM * 2];
bool flag[MAXN];
void link(int u, int v) {
Next[++ tot] = Last[u], Last[u] = tot, Go[tot] = v;
}
void dfs(int now, int pre) {
flag[now] = 1, fa[now] = pre;
deep[now] = deep[pre] + 1;
bel[++ tim] = now;
for (int p = Last[now]; p; p = Next[p])
if (!flag[Go[p]]) dfs(Go[p], now);
}
bool remake() {
dfs(1, 0);
for (int i = 1; i <= n; i ++) flag[i] = 0;
for (int i = 1; i <= m; i ++) {
int u = path[i].u, v = path[i].v;
if (deep[u] < deep[v]) swap(u, v);
if (fa[u] == v) continue;
tag[u] ++, tag[v] --;
}
for (int i = n; i; i --) {
int now = bel[i];
for (int p = Last[now]; p; p = Next[p]) {
int v = Go[p];
if (fa[v] != now) continue;
tag[now] += tag[v];
}
if (!tag[now]) flag[now] = 1;
if (tag[now] > 1) return printf("0\n"), 0;
}
for (int i = 1; i <= n; i ++) Last[i] = 0;
tot = 0;
for (int i = 1; i <= n; i ++)
if (flag[i] && fa[i]) link(i, fa[i]), link(fa[i], i);
return 1;
}
void dp(int now, int pre) {
flag[now] = 1;
f[now] = 1, g[now] = 0;
int num = 0;
for (int p = Last[now]; p; p = Next[p]) {
int v = Go[p];
if (v == pre) continue;
dp(v, now);
f[now] = 1ll * f[now] * g[v] % Mo;
num ++;
}
g[now] = (1ll * f[now] * h[num] % Mo + 1ll * f[now] * h[num - 1] % Mo * num % Mo) % Mo;
f[now] = 1ll * f[now] * h[num] % Mo;
}
void solve() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) Last[i] = tag[i] = flag[i] = 0;
h[0] = h[1] = 1;
for (int i = 2; i <= n; i ++)
h[i] = (h[i - 1] + 1ll * h[i - 2] * (i - 1) % Mo) % Mo;
tot = tim = 0;
for (int i = 1; i <= m; i ++) {
int u, v;
scanf("%d%d", &u, &v);
link(u, v), link(v, u);
path[i].u = u, path[i].v = v;
}
if (!remake()) return;
for (int i = 1; i <= n; i ++) flag[i] = 0;
int ans = 1;
for (int i = 1; i <= n; i ++) {
if (flag[i]) continue;
dp(i, 0);
ans = 1ll * ans * f[i] % Mo;
}
printf("%d\n", ans);
}
int main() {
int t;
scanf("%d", &t);
for (int i = 1; i <= t; i ++) solve();
}