题意:给一个图,问删除哪些边可以让原图变为二分图
继续旧题补档,这题当时打比赛加强到了$2000000$
一个图是二分图当且仅当它不含奇环,所以我们要做的就是删边以破坏奇环
对图dfs,得到dfs树,此时非树边只会是返祖边,返祖边+树上路径可以构成环,我们把这种环称为“简单环”
如果没有简单奇环,那么它本来就是二分图,删掉任意一条边还是二分图
如果只有$1$个简单奇环,那么构成这个简单奇环的返祖边是可以删的
一条树边能删当且仅当所有奇环经过它而且没有偶环经过它,因为既要破坏所有奇环又不能构成新的奇环
统计奇环偶环直接dfs时差分统计即可
#include<stdio.h> #include<algorithm> using namespace std; int h[2000010],to[4000010],nex[4000010],fa[2000010],dep[2000010],fae[2000010],d1[2000010],d2[2000010],s1[2000010],s2[2000010],ans[2000010],cnt,o,e,n,m,o1; void add(int a,int b,int i){ to[i]=b; nex[i]=h[a]; h[a]=i; i+=m; to[i]=a; nex[i]=h[b]; h[b]=i; } int down(int x){ if(x>m)x-=m; return x; } void dfs(int x){ for(int i=h[x];i;i=nex[i]){ if(dep[to[i]]==0){ dep[to[i]]=dep[x]+1; fa[to[i]]=x; fae[to[i]]=down(i); dfs(to[i]); s1[x]+=s1[to[i]]; s2[x]+=s2[to[i]]; }else if(to[i]!=fa[x]&&dep[x]>dep[to[i]]){ if((dep[x]-dep[to[i]])&1){ e++; d2[x]++; d2[to[i]]--; }else{ o++; d1[x]++; d1[to[i]]--; o1=down(i); } } } s1[x]+=d1[x]; s2[x]+=d2[x]; } int rev(int x){ if(x>m)return x-m; return x+m; } int gao(int x){ if(fa[to[x]]==to[rev(x)])return to[x]; if(fa[to[rev(x)]]==to[x])return to[rev(x)]; return 0; } int main(){ int i,x,y; scanf("%d%d",&n,&m); for(i=1;i<=m;i++){ scanf("%d%d",&x,&y); add(x,y,i); } for(i=1;i<=n;i++){ if(dep[i]==0){ dep[i]=1; dfs(i); } } if(o==0){ printf("%d\n",m); for(i=1;i<=m;i++)printf("%d ",i); return 0; } if(o==1){ cnt=1; ans[1]=o1; } for(i=1;i<=m;i++){ x=gao(i); if(x){ if(s1[x]==o&&s2[x]==0){ cnt++; ans[cnt]=i; } } } printf("%d\n",cnt); sort(ans+1,ans+cnt+1); for(i=1;i<=cnt;i++)printf("%d ",ans[i]); }