【CF19E】Fairy

题意

给定一个无向图,问删掉那条边使得给图可以变成一个二分图。

思路

回顾二分图的定义:不存在奇环的图。
由于不保证连通图,所以可以把整个图分成若干个连通块来考虑。

  1. 若所有连通块都是二分图:则此时删掉哪一条边剩下的都能形成二分图,答案是 \(m\).
  2. 若存在两个及以上的连通块是二分图:则此时不合法,删掉那一条边都一定会有二分图。

限制掉前两种后,接下来考虑只有一个连通块是二分图,于是可以转化为一个连通图的问题。
若删去一条边使得删完后不存在奇环,则删去的边一定满足以下条件:

  1. 在所有奇环中。
    如果删去的边不在其中一个奇环里面,则连通图一定还存在一个奇环,此时不可能为二分图。
  2. 不在任意一个偶环中。
    考虑要删的一条边,一定满足以下性质:
    First.一定连通两个连通块。
    Second.一定有一条另外的边联通两个连通块,形成一个奇环。
    考虑到如果删去这条边的效果是可以给其中一个连通块的点都变色,才能使得染色不矛盾。
    而此时若存在一个偶环,则变完色后偶环的点又会有矛盾了,即证。

然后接下来就是怎么来写了。
可以把连通图以 \(DFS\) 树的方式显现,则每一个环都存在一条返祖边,然后就可以用树上差分做了。

code

#include<bits/stdc++.h>
#define ll long long

using namespace std;

const int N = 1e4+5;

struct Edge{
	int to,nxt,id;
}e[N<<1];

int tot=0,cnt=0,res=0;
int head[N],dep[N],s[N],fa[N],p;
bool flag=0;
bool vis[N];
vector<ll> vec;

void add_Edge(int u,int v,int id){
	e[tot].to=v,e[tot].nxt=head[u],e[tot].id=id;
	head[u]=tot++;
}

void dfs(int u,int f){
	dep[u]=dep[f]+1;
	fa[u]=f;
	vis[u]=1;
	for(int i=head[u];~i;i=e[i].nxt) {
		int v=e[i].to;
		if(v==f)continue;
		if(vis[v]){
			if(dep[v]<dep[u]){
				ll siz=dep[u]-dep[v]+1;
				if(siz&1)s[u]++,s[v]--,res++,flag=1,p=e[i].id;
				else s[u]--,s[v]++;
			}
		}
		else dfs(v,u);
	}
}

void DFS(int u){
	vis[u]=1;
	for(int i=head[u];~i;i=e[i].nxt){
		int v=e[i].to,idx=e[i].id;
		if(vis[v])continue;
		DFS(v);
		s[u]+=s[v];
		if(s[v]==res)vec.push_back(idx);
	}
}
int n,m;
int main() {
	memset(head,-1,sizeof(head));
	cin>>n>>m;
	
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		add_Edge(u,v,i);
		add_Edge(v,u,i);
	}
	
	for(int i=1;i<=n;i++){
		flag=0;
		if(!vis[i])dfs(i,0);
		if(flag){
			cnt++;
			if(cnt>1){
				cout<<-1;
				return 0;
			}
			memset(vis,0,sizeof(vis));
			DFS(i);
		}
	}
	if(cnt){
		if(res==1)vec.push_back(p);
		cout<<vec.size()<<"\n";
		sort(vec.begin(),vec.end());
		for(int i=0;i<vec.size();i++)
			cout<<vec[i]<<" ";
	}
	else {
		cout<<m<<"\n";
		for(int i=1;i<=m;i++)
			cout<<i<<" ";
	}
	return 0;
}
posted @ 2025-09-29 18:19  Harvey-zhuhy  阅读(6)  评论(0)    收藏  举报