【CF19E】Fairy
题意
给定一个无向图,问删掉那条边使得给图可以变成一个二分图。
思路
回顾二分图的定义:不存在奇环的图。
由于不保证连通图,所以可以把整个图分成若干个连通块来考虑。
- 若所有连通块都是二分图:则此时删掉哪一条边剩下的都能形成二分图,答案是 \(m\).
- 若存在两个及以上的连通块是二分图:则此时不合法,删掉那一条边都一定会有二分图。
限制掉前两种后,接下来考虑只有一个连通块是二分图,于是可以转化为一个连通图的问题。
若删去一条边使得删完后不存在奇环,则删去的边一定满足以下条件:
- 在所有奇环中。
如果删去的边不在其中一个奇环里面,则连通图一定还存在一个奇环,此时不可能为二分图。 - 不在任意一个偶环中。
考虑要删的一条边,一定满足以下性质:
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;
}