CF19E-Fairy【树形结构,差分】

正题

题目链接:https://www.luogu.com.cn/problem/CF19E


题目大意

给出\(n\)个点\(m\)条边的一张无向图,求有多少条边去掉后可以使得图变成一张二分图。

\(1\leq n,m\leq 10^4\)


解题思路

虽然线段树分治可以暴力草过去但是考虑点智慧的做法。

众所周知没有奇环是图是二分图的充要条件,所以答案就是奇环的交。

显然无法考虑所有的奇环,但是我们可以考虑一下简单奇环。

先随便跑出一个生成树,然后枚举所有非树边考虑其在树上的环,只考虑树边的话答案肯定是所有简单奇环的交。并且这条边不能出现在一个枚举出的偶环上,因为如果这条边即在一个奇环又在一个偶环上,那么这两个环的不交部分一定是一个奇环,所以这条边就不成立了。

然后对于非树边,显然条件就是这条边必须得是唯一一个树上奇环的边。

\(dfs\)搜出生成树,此时所有的非树边都是返祖边,这样我们就可以用树上差分计算树边的条件了。

时间复杂度\(O(n+m)\)(不算答案用的排序)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e4+10;
struct node{
	int to,next,w;
}a[N<<1];
int n,m,tot,cnt,ant,last;
int ls[N],v[N],s[N],from[N],ans[N];
void addl(int x,int y,int w){
	a[++tot].to=y;
	a[tot].next=ls[x];
	a[tot].w=w;
	ls[x]=tot;return;
}
void dfs(int x){
	for(int i=ls[x];i;i=a[i].next){
		int y=a[i].to;
		if((i^1)==from[x])continue;
		if(v[y]&&v[x]>v[y]){
			if((v[x]-v[y])&1)s[x]--,s[y]++;
			else s[x]++,s[y]--,cnt++,last=a[i].w;
		}
		else if(!v[y]){
			v[y]=v[x]+1;
			from[y]=i;
			dfs(y);
			s[x]+=s[y];
		}
	}
	return;
}
int main()
{
	scanf("%d%d",&n,&m);tot=1;
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		addl(x,y,i);addl(y,x,i);
	}
	for(int i=1;i<=n;i++)
		if(!v[i])v[i]=1,dfs(i);
	if(!cnt){
		printf("%d\n",m);
		for(int i=1;i<=m;i++)
			printf("%d ",i);
		return 0;
	}
	if(cnt==1)ans[++ant]=last;
	for(int i=1;i<=n;i++)
		if(s[i]==cnt)ans[++ant]=a[from[i]].w;
	sort(ans+1,ans+1+ant);
	ant=unique(ans+1,ans+1+ant)-ans-1;
	printf("%d\n",ant);
	for(int i=1;i<=ant;i++)
		printf("%d ",ans[i]);
	return 0;
}
posted @ 2021-08-24 11:28  QuantAsk  阅读(32)  评论(0编辑  收藏  举报