F - Forest Program( DFS找环的个数和长度 )

F - Forest Program( DFS找环的个数和长度 )

The kingdom of Z is fighting against desertification these years since there are plenty of deserts in its wide and huge territory. The deserts are too arid to have rainfall or human habitation, and the only creatures that can live inside the deserts are the cactuses. In this problem, a cactus in desert can be represented by a cactus in graph theory.

In graph theory, a cactus is a connected undirected graph with no self-loops and no multi-edges, and each edge can only be in at most one simple cycle. While a tree in graph theory is a connected undirected acyclic graph. So here comes the idea: just remove some edges in these cactuses so that the remaining connected components all become trees. After that, the deserts will become forests, which can halt desertification fundamentally.

Now given an undirected graph with n vertices and m edges satisfying that all connected components are cactuses, you should determine the number of schemes to remove edges in the graph so that the remaining connected components are all trees. Print the answer modulo 998244353.

Two schemes are considered to be different if and only if the sets of removed edges in two schemes are different.

Input

The first line contains two non-negative integers n,m (1≤n≤300000,0≤m≤500000), denoting the number of vertices and the number of edges in the given graph.

Next m lines each contains two positive integers u,v (1≤u,v≤n,u≠v), denoting that vertices u and v are connected by an undirected edge.

It is guaranteed that each connected component in input graph is a cactus.

Output

Output a single line containing a non-negative integer, denoting the answer modulo 998244353.

Examples Input

3 3
1 2
2 3
3 1

Output

7

Input

6 6
1 2
2 3
3 1
2 4
4 5
5 2

Output

49

传送门

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

分析:由于原题规定一条边只属于一个环,不需要考虑环套环。每一种方案删除之后不能存在环,所以对于图中所有环,设环的边数为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。

理解:对于一个连通图来说,只要从一个点进入bfs,那么整个图的所有环就都能求出来。具体可以把样例画出来debug一下理解。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll mod = 998244353;
const int maxn = 3e6+10;
int n,m,cnt;
ll ans,sum;
int via[maxn];
int head[maxn];
int pre[maxn];
struct node {
    int to,nxt;
}e[maxn];

void addage( int u, int v ) // 建边直接建双向的
{
    e[cnt].to = v;
    e[cnt].nxt = head[u];
    head[u] = cnt++;
    e[cnt].to = u;
    e[cnt].nxt = head[v];
    head[v] = cnt++;
}

ll qpow( ll a, ll n )
{
    ll re = 1;
    while ( n ) {
        if ( n&1 ) re=(re*a)%mod;
        a = (a*a)%mod;
        n>>=1;
    }
    return re;
}

void dfs( int x, int father )
{
    via[x] = 1; // 当前点正在走
    for ( int i=head[x]; i!=-1; i=e[i].nxt ) {
        int y = e[i].to;
        if ( y==father ) {
            continue ;
        }
        if ( via[y]==0 ) {
            pre[y] = x; // 记录父节点
            dfs(y,x);
        }
        else if ( via[y]==1 ) { // 又到了正在走的点,看到是成环的
            int t = x;
            int tot = 1; // 环的大小
            while ( t!=y ) {
                tot ++;
                t = pre[t];
            }
            sum += tot; // 所有环的边数
            ans = (ans * (qpow(2,tot) - 1)) % mod; // 当前环对答案的贡献值
        }
    }
    via[x] = 2; // 当前点走完了
}

int main()
{
    cin >> n >> m;
    memset(head,-1,sizeof(head));
    memset(via,0,sizeof(via));
    cnt = 0;
    for ( int i=1; i<=m; i++ ) {
        int u,v;
        scanf("%d %d",&u,&v);
        addage(u,v);
    }
    ans = 1,sum=0;
    for ( int i=1; i<=n; i++ ) {
        if ( via[i]==0 ) {
            dfs(i,-1);
        }
    }
    printf("%lld\n", qpow(2,m - sum) * ans % mod); //  qpow(2,m - sum)是指杂边的贡献值

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值