題面
題解
實際上是各個環之間的森林上的鏈不重複覆蓋邊的問題。
原問題是不能覆蓋重邊的,但是我們這裏假設可以覆蓋重邊,一條邊不覆蓋就等價於覆蓋一條重邊,
那麼問題轉化爲覆蓋樹上所有邊有多少種方法。
注意到樹上某個點的方案數與其他點無關,而只與自己的度數有關,也就是說一個點的所有邊進行不同的匹配都對應了一種不同的覆蓋方案。
那麼設\(f_i\)表示度數爲\(i\)的點相鄰的邊的匹配方案數,那麼有\(f_i=f_{i-1}+(i-1)\times f_{i-2}\),
邊界條件\(f_0=f_1=1\)。
那麼答案就是\(\prod_{i\in \text{森林}} f_i\)。
代碼
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
inline int gi() {
register int data = 0, w = 1;
register char ch = 0;
while (!isdigit(ch) && ch != '-') ch = getchar();
if (ch == '-') w = -1, ch = getchar();
while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar();
return w * data;
}
const int Mod = 998244353;
const int MAX_N = 5e5 + 5;
struct Graph { int to, next; } e[MAX_N << 2];
int fir[MAX_N], e_cnt;
void Add_Edge(int u, int v) { e[e_cnt] = (Graph){v, fir[u]}, fir[u] = e_cnt++; }
int N, M, f[MAX_N], deg[MAX_N];
int vis[MAX_N], dfn[MAX_N], fa[MAX_N], tim;
bool flag = 0;
void dfs(int x) {
dfn[x] = ++tim;
for (int i = fir[x]; ~i; i = e[i].next) {
int v = e[i].to;
if (v == fa[x]) continue;
if (!dfn[v]) fa[v] = x, dfs(v);
else if (dfn[v] > dfn[x]) {
deg[x] -= 2;
for (int u = v; u != x; u = fa[u]) {
if (vis[u]) return (void)(flag = 1);
vis[u] = 1, deg[u] -= 2;
}
}
if (flag) return ;
}
}
int main () {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin);
#endif
memset(fir, -1, sizeof(fir));
f[0] = f[1] = 1;
for (int i = 2; i <= 5e5; i++) f[i] = (f[i - 1] + 1ll * (i - 1) * f[i - 2]) % Mod;
int Q = gi();
while (Q--) {
N = gi(), M = gi();
for (int i = 1; i <= M; i++) {
int u = gi(), v = gi();
Add_Edge(u, v), Add_Edge(v, u);
deg[u]++, deg[v]++;
}
dfs(1);
if (flag) puts("0");
else {
int ans = 1;
for (int i = 1; i <= N; i++) ans = 1ll * ans * f[deg[i]] % Mod;
printf("%d\n", ans);
}
for (int i = 1; i <= N; i++) vis[i] = dfn[i] = deg[i] = 0, fir[i] = -1;
flag = 0, e_cnt = 0;
}
return 0;
}