CCPC2019QHD Forest Program

题目链接
2019CCPC秦皇岛赛站题目

问题重述

给你一个由仙人掌树构成的图G,求G有多少种删边方案能使其成为森林。

计数算法

区分边为环上边非环上边

  1. 环上边至少删掉一个,剩下的随意。长度为 L 的环对答案贡献 2 L − 1 2^L - 1 2L1
  2. 非环上边,可删可不删,那就是 2 m − ∑ L 2^{m-\sum L} 2mL

然后这个题就完事了。判断环就用类似于Tarjan的那种方法,dep改一下就行了。
哦对了,这个2的次方需要提前处理出来。

代码

#include<bits/stdc++.h>
using namespace std;

#define MOD 998244353
const int N = 3e5+17, M = 1e6+17;
int fr[N], nxt[M], to[M], tails;
void add_one(int f, int t){
	nxt[++tails] = fr[f];
	fr[f] = tails;to[tails] = t;
}
void add(int f, int t){
	add_one(f,t); add_one(t,f);	
}

int n, m, dep[N], in_cycle;
long long ans, fac[N];

void dfs(int start, int fat){
	for(int v,p=fr[start];p;p=nxt[p]){
		if((v=to[p]) == fat)	continue;
		if(dep[v]){
			if(dep[v] > dep[start])	continue;
			ans = ans*(fac[dep[start]-dep[v]+1]-1)%MOD;
			in_cycle += dep[start] - dep[v] + 1;
		}else{
			dep[v] = dep[start] + 1;
			dfs(v, start);
		}
	}
}

void work(){
	tails = 0; ans = 1; in_cycle = 0;
	//scanf("%d%d",&n,&m);
	memset(dep, 0, (n+1)*sizeof(int));
	memset(fr, 0, (n+1)*sizeof(int));
	for(int i=1,p1,p2;i<=m;++i){
		scanf("%d%d",&p1,&p2);
		add(p1, p2);
	}
	for(int i=1;i<=n;++i){
		if(dep[i])	continue;
		dep[i] = 1;	dfs(i, 0);
	}
	ans = ans*fac[m-in_cycle]%MOD;
	printf("%lld\n",ans);
}

int main(){
	fac[0] = 1;
	for(int i=1;i<M;++i)
		fac[i] = (fac[i-1]<<1)%MOD;
	while(scanf("%d%d",&n,&m)==2)
		work();
	return 0;
}
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页