Description
给定n个点,m条边的无向图(无自环),可以从图中删除一条边,问删除哪些边可以使图变成一个二分图。
Input
第1行包含两个整数n,m,分别表示点数和边数。
第2~m+1行每行两个数x,y,表示有一条边连接点x,y。
Output
第一行两个整数,表示能删除的边的个数。
接下来一行按照从小到大的顺序输出能删除的边的编号。
Solution
这题的弱化版:https://blog.csdn.net/tylon2006/article/details/100556029
简直就是无脑CDQ,直接加边。。。
Code
#include<bits/stdc++.h>
using namespace std;
int f[2000010],siz[2000010],col[2000010],top,cnt,res,n,m;
int edge[2000010][2];
bool ans[2000010];
struct data{
int u,v,fau,fav,colu,colv,sizu,sizv;
}stk[2000010],tmp;
int find(int x){
if(x!=f[x]) return find(f[x]);
return x;
}
int findcol(int x){
if(x==f[x]) return 0;
return col[x]^findcol(f[x]);
}
bool merge(int x,int y){
int fax=find(x),fay=find(y);
int colx=findcol(x),coly=findcol(y);
if(fax==fay){
if(colx==coly) return 0;
return 1;
}
int fa=fax,son=fay;
if(siz[fax]<siz[fay]) swap(fa,son);
stk[++top]=(data){fa,son,f[fa],f[son],col[fa],col[son],siz[fa],siz[son]};
if(colx==coly) col[son]^=1;
siz[fa]+=siz[son];
f[son]=fa;
return 1;
}
bool work(int l,int r){
for(int i=l;i<=r;i++)
if(!merge(edge[i][0],edge[i][1])) return 0;
return 1;
}
void init(int x){
while(top>x){
tmp=stk[top]; top--;
f[tmp.u]=tmp.fau;
f[tmp.v]=tmp.fav;
col[tmp.u]=tmp.colu;
col[tmp.v]=tmp.colv;
siz[tmp.u]=tmp.sizu;
siz[tmp.v]=tmp.sizv;
}
}
void solve(int l,int r,bool flag){
//cout<<l<<" "<<r<<endl;
if(l==r){
if(flag) res++;
ans[l]=flag;
return;
}
int last=top,mid=(l+r)>>1;
bool now=flag&&work(mid+1,r);
solve(l,mid,now); init(last);
now=flag&&work(l,mid);
solve(mid+1,r,now); init(last);
}
inline int read(){
char c=getchar(); int x=0,f=1;
while(!isdigit(c) && c!='-') c=getchar();
if(c=='-') { f=-1; c=getchar(); }
while(isdigit(c)) { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); }
return x*f;
}
int main(){
int x,y;
n=read(),m=read();
for(int i=1;i<=n;i++){
siz[i]=1;
f[i]=i;
}
for(int i=1;i<=m;i++)
edge[i][0]=read(),edge[i][1]=read();
solve(1,m,1);
printf("%d\n",res);
for(int i=1;i<=m;i++)
if(ans[i]) printf("%d ",i);
}