题目链接:Gym 102361F
考虑每个简单环内至少取一条边,如果一个简单环有 \(x\) 条边,那么这个简单环的方案数为 \(\sum_{i=1}^{x} {x \choose i} = 2^x - 1\),对于非简单环上的边(即剩余的边)如果有 \(r\) 条,那么方案数为 \(\sum_{i=1}^{r} {r \choose i} = 2^r\),最后分步计数即可。
需要注意的是,这里没有保证整张图是联通的,只保证每个联通块是一个仙人掌,所以不能只DFS一个点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #include <bits/stdc++.h> using namespace std; typedef long long LL; const int P = 998244353; inline LL getPow(LL x, LL y) { LL ret = 1; while (y) { if (y & 1) ret = ret * x % P; x = x * x % P; y >>= 1; } return ret; } const int MAX_N = 300000 + 5; int n, m; vector <int> g[MAX_N]; vector <int> cir; stack <int> stk; int pos[MAX_N]; int ins[MAX_N]; int vis[MAX_N]; int eCnt; void dfs(int x, int fa) { stk.push(x); ins[x] = 1; pos[x] = stk.size(); for (auto const &e: g[x]) { if (e == fa) { continue; } else if (ins[e]) { cir.push_back(pos[x] - pos[e] + 1); eCnt += (pos[x] - pos[e] + 1); } else if (!vis[e]) { dfs(e, x); } } stk.pop(); ins[x] = 0; vis[x] = 1; } int main() { scanf("%d%d", &n, &m); int u, v; for (int i = 1; i <= m; ++i) { scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } for (int i = 1; i <= n; ++i) if (!vis[i]) dfs(i, -1); LL ans = 1; for (unsigned i = 0; i < cir.size(); ++i) { LL con = (((getPow(2, cir[i]) - 1) % P) + P) % P; ans = ans * con % P; } ans = ans * getPow(2, m - eCnt) % P; printf("%lld\n", ans); return 0; }
|