ZJOI2017 仙人掌
阿新 • • 發佈:2018-12-13
getchar clu 如果 \n lse ++ 轉移 sign ans
題目大概是給個無向圖,求添加邊使其變為仙人掌的方案數。
直接判斷是否仙人掌,特判輸出0即可。
否則的話,把環拆開成鏈變成一個樹,考慮暴力計算兒子的配對方案數,打表規律:\(f[i] = f[i - 1] + (i - 1) \times f[i - 2]\)
其實也可以推一推,如果不連邊就是\(f[i - 1]\),如果連邊,不妨設連得點是\(j\),那麽\(j\)就不能連向其他邊了(具體見仙人掌定義),所以有\((i - 1) \times f[i - 2]\)種選法。
方案數加和即可。
提前預處理出\(f\)數組,進行簡單的dp轉移即可。
小操作:快讀打崩了調了一個多小時才發現...
// luogu-judger-enable-o2 #include <bits/stdc++.h> using namespace std; const int mod = 998244353; #define int long long const int MAXN = 5e5 + 10; int deg[MAXN << 1]; struct edge { int to; int nxt; }e[MAXN << 1]; int f[MAXN << 1]; int ans; int fa[MAXN << 1]; int head[MAXN << 1]; int cnt; int dfn[MAXN << 1]; int idx; int state[MAXN << 1]; void add(int u,int v) { e[++cnt].to = v; e[cnt].nxt = head[u]; head[u] = cnt; return; } void Add(int u,int v) { add(u,v); add(v,u); return; } int dfs(int now) { dfn[now] = ++idx; for(int i = head[now],it;i;i=e[i].nxt) { int y = e[i].to; if(!dfn[y]) { fa[y] = now; if(dfs(y) == 1) return 1; } else if(dfn[y] > dfn[now]) { for(it = y,deg[now]-=2;it != now;state[it] = 1,deg[it] -= 2,it = fa[it]) { if(state[it]) return 1; } } } return 0; } int read () { int q=0,f=1;char ch=getchar(); while(!isdigit(ch)){ if(ch == '-')f=-1;ch=getchar(); } while(isdigit(ch)) { q=q*10+ch-'0';ch=getchar(); } return q*f; } int T,n,m,x,y; signed main () { f[0] = 1; f[1] = 1; for(int i = 2;i <= MAXN - 10; ++i) { f[i] = (f[i - 1] + 1ll * (i - 1) * f[i - 2]) % mod; } T = read(); while(T--) { n = read(),m = read(); cnt = 0;idx = 0; for(int i = 1;i <= n; ++i) { head[i] = deg[i] = state[i] = 0; dfn[i] = 0; } for(int i = 1;i <= m; ++i) { x = read(),y = read(); deg[x] ++; deg[y] ++; Add(x,y); } if(dfs(1)) { puts("0"); } else { ans = 1; for(int i = 1;i <= n; ++i) { ans = 1ll * ans * f[deg[i]] % mod; } printf("%lld\n",ans); } } return 0; } /* 2 3 2 1 2 1 3 5 4 1 2 2 3 2 4 1 5 */
ZJOI2017 仙人掌