题意:给定一张无向简单图,同时规定一条边只属于一个环。可以删除任意条边使得这张图变成森林,也就是使得每一个连通块都是树。求一共有多少种方案。

#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <vector>
#include <stack>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stdlib.h>

/*
 用 DFS 暴力找环,然后乘法原理算一下即可。注意非环边也会有贡献。
 DFS 可以模仿 Tarjan 算法(离线算法)写。
 */

typedef long long LL;
const int maxn = 3e5 + 10;
using namespace std;
int u, v, n, m, path[maxn], vis[maxn];
LL ans = 1;
LL cnt, mod = 998244353;

LL sum = 0; //sum是环的节点一共有多少个
vector<int> g[maxn];

LL qpow(LL n)
{
    LL a=2;
    LL re=1;
    while(n)
    {
        if(n&1) //判断n的二进制最后一位是不是1,或者说判断n是不是奇数
        {
            re=(re*a)%mod;
        }
        n>>=1;  //除掉n二进制的最后一位
        a=(a*a)%mod;
    }
    return re%mod;
}


void dfs(int now, int pre)
{
    vis[now] = 1;//将当前这个点标记为遍历过
    for (int i = 0; i < g[now].size(); ++i) //将与now相连的点全都遍历一遍
    {
        int to = g[now][i];
        if (to == pre) continue; //如果to是自己的父亲节点(就是已经遍历过了),那么直接continue就可以了。
        if (vis[to] == 0)        //如果to并没有被遍历,那么对to这个节点进行遍历
        {
            path[to] = now;
            dfs(to, now);
        }
        else if (vis[to] == 2) continue; //如果这个点已经被遍历两次了
        else                    //如果vis[to]==1,也就是to这个节点已经被遍历过了,所以形成环(比如1,2,3之间两两相连,1-》2,2-〉3,3-》1)
        {
            int temp = now;     //设temp为当前这个点
            cnt = 1;            //cnt为最后成环的个数
            while (temp != to)  //如果temp不等于想要最后成环的点to,那么就寻找
            {
                ++cnt;
                temp = path[temp];
            }
            sum += cnt;        //成环的点加上cnt
            ans = (ans * (qpow(cnt) - 1)) % mod;
        }
    }
    vis[now] = 2; //在最后将now这个节点设为2
}
int main()
{
    while(~scanf("%d %d", &n, &m))  //分别代表点和边
    {
        memset(path, 0, sizeof(path));
        memset(vis, 0, sizeof(vis));
        for (int i=1;i<=m;i++)
        {
            g[i].clear();
        }
        sum=0;
        ans=1;
        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) //一共n个点,对这n个点都进行遍历一遍
        {
            if (vis[i] == 0) //如果这个点没有被遍历过,那么用dfs对这个点进行遍历一遍
            {
                dfs(i, 0);
            }
        }
        printf("%lld\n", qpow(m - sum) * ans % mod); //qpow(m-sum)代表的是无环的点可以进行删掉的个数
    }
    return 0;
}