2019 CCPC 秦皇岛F Forest Program(dfs)

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

分析:要将仙人掌变成树(或者森林),只需要保证对于仙人掌中的每个环,至少有一条边被删去即可。

设图中环的大小分别为c_{1},c_{2},...,c_{k},不属于任何一个环的边数为b, 则答案为:

2^{b}\prod_{i=1}^{k}(2^{c_{i}}-1)

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;
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值