Newcoder 40 C.珂朵莉的二分图(dfs+树形DP)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/V5ZSQ/article/details/83271287

Description

珂朵莉给你一个无向图

其有 n n 个点, m m 条边

对于每条边,她想知道删了这条边之后这个图是不是一个二分图

Input

第一行两个整数 n , m n,m

之后 m m 行,每行两个数 x , y x,y 表示有一条 x x y y 之间的无向边

i i 个边的序号即为 i i

( n , m 1 0 6 ) (n,m\le 10^6)

Output

第一行输出一个整数,表示有多少边满足条件

接下来一行,从小到大输出这些边的序号

如果没有边满足条件,只输出一行一个数 0 0 ,注意不要多输出换行

Sample Input

4 4
1 2
1 3
2 4
3 4

Sample Output

4
1 2 3 4

Solution

一个图是二分图当且仅当该图无奇环,若想让该图成为一个二分图,必须破掉所有奇环,那么就要选择所有奇环交集中的一条边,注意到一个奇环和一个偶环在删去一条公共边后依旧是一个奇环,故不能删去偶环上的边

具体做法,首先统计所有自环,如果又有奇环又有自环,显然不行;如果没有奇环但是有超过一个自环也不行;如果没有自环也没有奇环,删去任意一条边都行;如果没有自环只有奇环, d f s dfs 整张图,维护每个点的深度,如果当前点 u u 的一个邻接点 v v 之前被经过,且深度不超过 u u (如果深度超过 u u 只能是有重边,这种偶环不用管,因为这种边不会出现在奇环的交集中),那么说明 u , v u,v 之间构成一个环,且该环是由树上的 u , v u,v 这条链和当前的 u v uv 边构成,我们可以在树上维护一个标记的前缀和,标记 u , v u,v 这一段所有的边,表示这些边属于这个环,注意到可以通过 u , v u,v 深度差判断该环是奇环还是偶环,如果是奇环则打上正标记,否则打上负标记,假设共有 n u m num 个奇环,那么被打上 n u m num 个标记的边都是合法边,同时注意,若只有一个奇环,那么那条非树边也是合法边

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<ctime>
using namespace std;
const int maxn=1000005;
struct node
{
	int v,id,next;
}e[2*maxn];
int head[maxn],tot;
void add(int u,int v,int id)
{
	e[tot].v=v,e[tot].id=id,e[tot].next=head[u],head[u]=tot++;
}
int n,m,dep[maxn],num[maxn],vis[maxn],cir,self,not_tree;
vector<int>ans;
void dfs(int u,int fa)
{
	vis[u]=1;
	for(int i=head[u];~i;i=e[i].next)
	{
		if(i==fa)continue;
		int v=e[i].v;
		if(!vis[v])
		{
			dep[v]=dep[u]+1;
			dfs(v,i^1);
			num[u]+=num[v];
		}
		else
		{
			if(dep[v]>dep[u])continue;
			if((dep[u]-dep[v]+1)&1)num[u]++,num[v]--,cir++,not_tree=e[i].id;
			else num[u]--,num[v]++;
		}
	}
}
void dfs1(int u,int fa)
{
	vis[u]=1;
	if(num[u]==cir)ans.push_back(e[fa].id);
	for(int i=head[u];~i;i=e[i].next)
		if(!vis[e[i].v])dfs1(e[i].v,i);
}
int main()
{
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	tot=0;
	for(int i=1;i<=m;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		if(u==v)self++,ans.push_back(i);
		else add(u,v,i),add(v,u,i);
	}
	for(int i=1;i<=n;i++)
		if(!vis[i])
		{
			dep[i]=1;
			dfs(i,-1);
		}
	if(!cir)
	{
		if(self==0)
		{
			printf("%d\n",m);
			for(int i=1;i<=m;i++)printf("%d%c",i,i==m?'\n':' ');
		}
		else if(self==1)printf("1\n%d\n",ans[0]);
		else printf("0\n");
	}
	else
	{
		if(self)printf("0\n");
		else
		{
			memset(vis,0,sizeof(vis));
			for(int i=1;i<=n;i++)
				if(!vis[i])dfs1(i,-1);
			if(cir==1)ans.push_back(not_tree);
			printf("%d\n",ans.size());
			sort(ans.begin(),ans.end());
			for(int i=0;i<ans.size();i++)printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/V5ZSQ/article/details/83271287