Description
给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成
一个二分图。
Input
第 1 行包含两个整数 n,m。分别表示点数和边数。
第 2 到 m+1 行每行两个数 x,y 表示有一条(x,y)的边。
Output
输出第一行一个整数,表示能删除的边的个数。
接下来一行按照从小到大的顺序输出边的序号。
Sample Input
1 2
1 3
2 4
3 4
Sample Output
1 2 3 4
HINT
100%的数据,n,m<=1000000
题解Here!
BZOJ4025: 二分图
- 如果$flag==0$,即没有奇环。
只要把所有边序号输出即可。
- 如果$flag==1$,即有一个奇环。
那么我们的一个选择是把该条矛盾边删掉,或者是在该矛盾边$(u,v)$的树上路径上找一些边,满足这些边不被合法边覆盖。
至于为什么要让其不被合法边覆盖,画个图感性理解一下。。。
这样可以保证该矛盾边不会与其他非矛盾边再形成奇环。
- 如果$flag>1$,即有若干个奇环。
那么我们只能选择删除树上那些,被所有矛盾边覆盖,且不被非矛盾边覆盖的边了。
但是如何判断呢?
我们可以利用树上差分的思想,记$delta[x]$为$x$和其父亲构成的边上的差分值,把矛盾边的路径$+1$,非矛盾边的路径上$-1$。
那么最后树上的点$x$如果满足$delta[x]==flag$,那么它与它的父亲组成的边就是我们所求的。
注意:图有可能不连通,并且会有重边与自环!
我考虑了这些,但是在$BZOJ$上光荣$WA$了。。。
我不服,“上诉”到$codeforces$,然后$AC$。。。
所以$BZOJ$上的是个什么鬼数据啊。。。我交了几个网上的题解好像还有没过的。。。
所以这个题就这样吧,有时间再来改。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define MAXN 1000010
using namespace std;
int n,m,c=1,d=0,e=1,flag=0;
int start,end,ancester,id;
int head[MAXN],h[MAXN],deep[MAXN],fa[MAXN],father[MAXN],vis[MAXN],delta[MAXN];
int last[MAXN],ans[MAXN];
struct Edge{
int x,y;
}edge[MAXN];
struct Tree{
int next,to,id;
}tree[MAXN<<1],question[MAXN<<1];
struct Question{
int x,y,id,lca;
}que[MAXN];
inline int read(){
int date=0;char c=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date;
}
int find(int x){return father[x]==x?x:father[x]=find(father[x]);}
void uniun(int x,int y){x=find(x);y=find(y);if(x!=y)father[y]=x;}
inline void add(int x,int y,int i){
tree[c].to=y;tree[c].id=i;tree[c].next=head[x];head[x]=c++;
tree[c].to=x;tree[c].id=i;tree[c].next=head[y];head[y]=c++;
}
inline void add_que(int x,int y,int i){
question[e].to=y;question[e].id=i;question[e].next=h[x];h[x]=e++;
question[e].to=x;question[e].id=i;question[e].next=h[y];h[y]=e++;
}
void kruskal(){
for(int i=1;i<=n;i++)father[i]=i;
for(int i=1;i<=m;i++){
if(find(edge[i].x)!=find(edge[i].y)){
uniun(edge[i].x,edge[i].y);
add(edge[i].x,edge[i].y,i);
}
else{
d++;
que[d]=(Question){edge[i].x,edge[i].y,i,0};
add_que(edge[i].x,edge[i].y,d);
}
}
}
void LCA(int x){
vis[x]=1;
for(int i=head[x];i;i=tree[i].next){
int v=tree[i].to;
if(!vis[v]){
deep[v]=deep[x]+1;
fa[v]=x;
last[v]=tree[i].id;
LCA(v);
uniun(x,v);
}
}
for(int i=h[x];i;i=question[i].next){
int v=question[i].to;
if(father[v])que[question[i].id].lca=find(v);
}
}
void get_sum(int x){
int v;
for(int i=head[x];i;i=tree[i].next){
v=tree[i].to;
if(!vis[v]){
vis[v]=1;
get_sum(v);
delta[x]+=delta[v];
}
}
}
void work(){
if(!flag){
printf("%d\n",m);
for(int i=1;i<=m;i++)printf("%d ",i);
printf("\n");
}
else if(flag==1){
int top=0;
for(int i=start;i!=ancester;i=fa[i])if(delta[i]==1)ans[++top]=last[i];
for(int i=end;i!=ancester;i=fa[i])if(delta[i]==1)ans[++top]=last[i];
ans[++top]=id;
sort(ans+1,ans+top+1);
printf("%d\n",top);
for(int i=1;i<=top;i++)printf("%d ",ans[i]);
printf("\n");
}
else{
int top=0;
for(int i=1;i<=n;i++)if(delta[i]==flag)ans[++top]=last[i];
sort(ans+1,ans+top+1);
printf("%d\n",top);
for(int i=1;i<=top;i++)printf("%d ",ans[i]);
printf("\n");
}
}
void init(){
int x,y,lca,dis,v;
n=read();m=read();
for(int i=1;i<=m;i++){edge[i].x=read();edge[i].y=read();}
kruskal();
for(int i=1;i<=n;i++)father[i]=i;
for(int i=1;i<=n;i++)if(!vis[i]){deep[i]=1;LCA(i);}
for(int i=1;i<=d;i++){
x=que[i].x;y=que[i].y;lca=que[i].lca;v=1;
dis=deep[x]+deep[y]-2*deep[lca];
if(dis&1)v=-1;
else{
flag++;
start=x;end=y;ancester=lca;id=que[i].id;
}
delta[x]+=v;delta[y]+=v;delta[lca]-=2*v;
}
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)if(!vis[i]){vis[i]=1;get_sum(i);}
}
int main(){
init();
work();
return 0;
}