【题目链接】
【思路要点】
- 考虑给定的图为大小为 N + 1 N+1 N+1 的点的菊花图的情况,记方案数为 d p N dp_N dpN ,考虑最后一个儿子是否连边,则有转移 d p i = d p i − 1 + ( i − 1 ) d p i − 2 dp_i=dp_{i-1}+(i-1)dp_{i-2} dpi=dpi−1+(i−1)dpi−2
- 断开环边,剩余问题即为如何解决图是一棵树的情况。
- 注意到由于不允许存在重边,一个合法的加边方案中边的长度 ≥ 2 \geq2 ≥2 。
- 考虑对一个合法的加边方案进行如下转化:记一条边 ( x , y ) (x,y) (x,y) 经过了点 x , a , b , c , … , d , e , f , y x,a,b,c,\dots,d,e,f,y x,a,b,c,…,d,e,f,y ,则断开 ( x , y ) (x,y) (x,y) ,连接 ( x , b ) , ( a , c ) , … , ( d , f ) , ( e , y ) (x,b),(a,c),\dots,(d,f),(e,y) (x,b),(a,c),…,(d,f),(e,y) 。
- 得到的连边方案所连的新边长度均为 2 2 2 ,且与将各个点与其邻点看做一个菊花图的连边方案的组合一一对应。
- 因此,记 d i d_i di 为 i i i 的度数,答案即为 ∏ i = 1 N d p d i \prod_{i=1}^{N}dp_{d_i} i=1∏Ndpdi
- 时间复杂度 O ( N + M ) O(N+M) O(N+M) 。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5e5 + 5; const int P = 998244353; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, m, cnt[MAXN], dp[MAXN]; bool vis[MAXN], ban[MAXN], ins[MAXN], flg; vector <pair <int, int>> a[MAXN]; void dfs(int pos, int fa, int num) { cnt[pos] = 0; vis[pos] = true; ins[pos] = true; for (auto x : a[pos]) if (!vis[x.first]) { dfs(x.first, pos, x.second); cnt[pos] += cnt[x.first]; } else if (ins[x.first] && x.first != fa) { cnt[pos]++, cnt[x.first]--; ban[x.second] = true; } ins[pos] = false; if (cnt[pos] >= 2) flg = true; if (cnt[pos] >= 1) ban[num] = true; } int main() { int T; read(T); while (T--) { read(n), read(m); for (int i = 1; i <= n; i++) { a[i].clear(); vis[i] = false; } for (int i = 1; i <= m; i++) { int x, y; read(x), read(y); a[x].emplace_back(y, i); a[y].emplace_back(x, i); ban[i] = false; } flg = false; dfs(1, 0, 0); if (flg) { puts("0"); continue; } dp[0] = dp[1] = 1; for (int i = 2; i <= n; i++) dp[i] = (dp[i - 1] + 1ll * (i - 1) * dp[i - 2]) % P; int ans = 1; for (int i = 1; i <= n; i++) { int cnt = 0; for (auto x : a[i]) if (!ban[x.second]) cnt++; ans = 1ll * ans * dp[cnt] % P; } writeln(ans); } return 0; }