题意:给定一张无向简单图,同时规定一条边只属于一个环。可以删除任意条边使得这张图变成森林,也就是使得每一个连通块都是树。求一共有多少种方案。
分析:由于原题规定一条边只属于一个环,不需要考虑环套环。每一种方案删除之后不能存在环,所以对于图中所有环,设环的边数为s,删除边的数量从1,2,3……s都是合法的,所以对于一个环的方案数为2^s-1。对于许多环,方案数相乘取模。同时,非环边可以任意删,所以求出所有环之后,设非环边数量为t,删除环边总方案为ans,删除非环边方案为2^t,则最后答案应当是2^t*ans。以上所有运算取模。
找环可以使用dfs一遍求出。方法为:vis数组设置为三种状态,0表示未被访问过。1表示正在被访问,即边指向的结点是当前结点在dfs树上的祖先节点。2表示访问完毕。同时dfs的同时记录每一个结点的先驱path。如果边访问到了vis为1的数组,说明存在环,则通过path数组,从当前结点回跳到指向的结点,经过的步数为环的长度-1。
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <bits/stdc++.h> #define mp make_pair #define debug(x) cout << #x << ": " << x << endl #define pb push_back typedef long long LL; const int maxn = 3e5 + 10; const int inf = 0x3f3f3f3f; const LL INF = 0x3f3f3f3f3f3f3f3f; using namespace std; int u, v, n, m, path[maxn], vis[maxn]; LL ans = 1, cnt, mod = 998244353, sum = 0; vector<int> g[maxn]; LL qpow(LL x) { LL res = 1, temp = 2; while (x) { if (x & 1) { res = (res * temp) % mod; } temp = (temp * temp) % mod; x >>= 1; } return res % mod; } void dfs(int now, int pre) { vis[now] = 1; for (int i = 0; i < g[now].size(); ++i) { int to = g[now][i]; if (to == pre) continue; if (vis[to] == 0) { path[to] = now; dfs(to, now); } else if (vis[to] == 2) { continue; } else { int temp = now; cnt = 1; while (temp != to) { ++cnt; temp = path[temp]; } sum += cnt; ans = (ans * (qpow(cnt) - 1)) % mod; } } vis[now] = 2; } int main() { scanf("%d %d", &n, &m); 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] == 0) { dfs(i, 0); } } printf("%lld ", qpow(m - sum) * ans % mod); }