BZOJ4424: Cf19E Fairy
Description
给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成
一个二分图。
Input
第 1 行包含两个整数 n,m。分别表示点数和边数。
第 2 到 m+1 行每行两个数 x,y 表示有一条(x,y)的边。
Output
输出第一行一个整数,表示能删除的边的个数。
接下来一行按照从小到大的顺序输出边的序号。
Sample Input
4 4
1 2
1 3
2 4
3 4
1 2
1 3
2 4
3 4
Sample Output
4
1 2 3 4
1 2 3 4
HINT
100%的数据,n,m<=1000000
题解Here!
感觉和某个题好像啊:
BZOJ4025: 二分图
一个图是二分图当且仅当它没有奇环。
然后如果一个图已经是二分图,那么删除任意一条边都还是二分图。
于是我们考虑随便求出一棵生成树。
然后把非树边都拿出来,求出它们两个点的$LCA$和距离$dis$。
这样,我们可以尝试一条条把非树边放在树上,看是否会构成奇环,统计奇环条数$flag$。
以下将构成奇环的非树边叫做矛盾边,不会构成奇环的非树边叫做合法边。
一条树上边被非树上边$(u,v)$“覆盖”,即这条树上边在$u->v$的树上路径上。
然后分类讨论:
- 如果$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; }