题面
Sol
首先判断是能成为仙人掌
然后考虑(DP)
因为所有的环内不可能连边,那么直接删掉
变成一个森林
对每个树求出方案然后相乘就是答案
一个巧妙的转化:看成选取若干条路径恰好覆盖所有的树边的方案数
设(g[i])表示(i)个点两两配对的方案数
(g[i]=g[i-1]+g[i-2]*(i-1))
即要么不配对,要么选一个配对
设(f[i])表示子树的方案数
首先(f[u]=Pi_{vin{son(u)}} f[v])
设(sum)为(u)的儿子数
如果(u)不为根,那么还可以有一个点向上匹配(f[u]+=g[sum+1])
否则(f[u]+=g[sum])
# include <bits/stdc++.h>
# define IL inline
# define RG register
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
IL int Input(){
RG int x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
const int maxn(5e5 + 5);
const int mod(998244353);
int n, m, dfn[maxn], low[maxn], idx, g[maxn], f[maxn], cactus, sta[maxn], top, col[maxn];
bool vis[maxn << 2];
struct Edge{
int first[maxn], cnt, nxt[maxn << 2], to[maxn << 2];
IL void Init(){
cnt = 0, Fill(first, -1);
}
IL void Add(RG int u, RG int v){
vis[cnt] = 0, nxt[cnt] = first[u], to[cnt] = v, first[u] = cnt++;
}
} e1;
IL void Tarjan(RG int u, RG int fe){
dfn[u] = low[u] = ++idx, sta[++top] = u;
RG int flg = 0;
for(RG int e = e1.first[u]; e != -1; e = e1.nxt[e]){
if(e == fe) continue;
RG int v = e1.to[e];
if(!dfn[v]){
Tarjan(v, e ^ 1), low[u] = min(low[u], low[v]);
if(low[v] < dfn[u]){
if(flg) cactus = 0;
flg = 1;
}
}
else{
low[u] = min(low[u], dfn[v]);
if(dfn[v] < dfn[u]){
if(flg) cactus = 0;
flg = 1;
}
}
}
while(low[u] == dfn[u]){
col[sta[top--]] = u;
if(sta[top + 1] == u) break;
}
}
IL void Upd(RG int &x, RG int y){
x += y;
if(x >= mod) x -= mod;
}
IL void Solve(RG int u, RG int rt){
f[u] = dfn[u] = 1; RG int sum = 0;
for(RG int e = e1.first[u]; e != -1; e = e1.nxt[e]){
RG int v = e1.to[e];
if(dfn[v] || vis[e]) continue;
Solve(v, rt);
f[u] = 1LL * f[u] * f[v] % mod;
++sum;
}
if(u != rt) f[u] = 1LL * f[u] * g[sum + 1] % mod;
else f[u] = 1LL * f[u] * g[sum] % mod;
}
int main(){
for(RG int t = Input(); t; --t){
e1.Init(), cactus = 1, top = idx = 0, n = Input(), m = Input();
for(RG int i = 1; i <= n; ++i) col[i] = dfn[i] = low[i] = f[i] = 0;
for(RG int i = 1, a, b; i <= m; ++i)
a = Input(), b = Input(), e1.Add(a, b), e1.Add(b, a);
g[0] = g[1] = 1;
for(RG int i = 2; i <= n + 1; ++i)
g[i] = g[i - 1], Upd(g[i], 1LL * (i - 1) * g[i - 2] % mod);
Tarjan(1, -1);
if(!cactus){
puts("0");
continue;
}
for(RG int i = 1; i <= n; ++i)
for(RG int e = e1.first[i]; e != -1; e = e1.nxt[e])
if(col[i] == col[e1.to[e]]) vis[e] = 1;
for(RG int i = 1; i <= n; ++i) dfn[i] = 0;
RG int ans = 1;
for(RG int i = 1; i <= n; ++i)
if(!f[i]) Solve(i, i), ans = 1LL * ans * f[i] % mod;
printf("%d
", ans);
}
return 0;
}