题意:给定一张无向简单图,同时规定一条边只属于一个环,并且没有自环和多重边。可以删除任意条边使得这张图变成森林,也就是使得每一个连通块都是树。求一共有多少种方案。
分析:要将仙人掌变成树(或者森林),只需要保证对于仙人掌中的每个环,至少有一条边被删去即可。
设图中环的大小分别为,不属于任何一个环的边数为b, 则答案为:
0表示未被访问过,-1表示还在访问当中且尚未返回,1表示访问完毕。
这种求环的算法思想和刘汝佳介绍拓扑排序的方法很相似啊,当时就是没想到啊~~~555
AC代码:
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
const int N = 300005, M = 500005 * 2;
const long long MOD = 998244353;
int head[N], ver[M], ne[M], path[N], visited[N];
int n, m, idx, cnt;//cnt 为环中的边数
ll ans;
ll quick_pow(int k){
ll res = 1, x = 2;
while(k){
if(k & 1) res = res * x % MOD;
x = x * x % MOD;
k >>= 1;
}
return res;
}
void add(int x, int y){
ver[idx] = y, ne[idx] = head[x], head[x] = idx ++;
}
void dfs(int x, int fa){
visited[x] = -1;
for(int i = head[x]; i != -1; i = ne[i]){
int y = ver[i];
if(y == fa || visited[y] == 1) continue;
if(visited[y] == 0){
path[y] = x;
dfs(y, x);
}
else{
int num = 1, p = x;
while(p != y){
p = path[p];
num ++;
}
ans = ans * (quick_pow(num) - 1) % MOD;
cnt += num;
}
}
visited[x] = 1;
}
int main(){
//freopen("1.txt", "r", stdin);
while(~scanf("%d%d", &n, &m)){
memset(head, -1, sizeof(head));
memset(visited, 0, sizeof(visited));
memset(path, -1, sizeof(path));
idx = 0, ans = 1, cnt = 0;
for(int i = 0; i < m; i ++){
int x, y;
scanf("%d%d", &x, &y);
add(x, y), add(y, x);
}
for(int i = 1; i <= n; i ++){
if(!visited[i]) dfs(i, 0);
}
printf("%lld\n", ans * quick_pow(m - cnt) % MOD);
}
return 0;
}