1. 程式人生 > >Bzoj4784: [Zjoi2017]仙人掌

Bzoj4784: [Zjoi2017]仙人掌

return add include 就是 ++ 森林 == reg %d

題面

傳送門

Sol

首先判斷是能成為仙人掌

然後考慮\(DP\)
因為所有的環內不可能連邊,那麽直接刪掉
變成一個森林
對每個樹求出方案然後相乘就是答案

一個巧妙的轉化:看成選取若幹條路徑恰好覆蓋所有的樹邊的方案數

\(g[i]\)表示\(i\)個點兩兩配對的方案數
\(g[i]=g[i-1]+g[i-2]*(i-1)\)
即要麽不配對,要麽選一個配對

\(f[i]\)表示子樹的方案數
首先\(f[u]=\Pi_{v\in{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\n", ans); } return 0; }

Bzoj4784: [Zjoi2017]仙人掌