bzoj 4424: Cf19E Fairy

来源:互联网 发布:模糊查询sql语句 编辑:程序博客网 时间:2024/05/16 18:36

题意:

给定 n 个点,m 条边的无向图,可以从图中删除一条边,问删除哪些边可以使图变成一个二分图。

题解:

关于二分图的性质:是一个要么无环,要么有偶环的图。
所以显然,在删掉那一条边后这个图不能有奇环。
我们定义奇环上不在生成树上的那一条边为返祖边。
则有一下几种情况:
1:没有奇环,则删任意一条边都可以。
2:只有一个奇环,那么可以删这个奇环上的任意一条边,且那条边不在偶环上。
为什么要考虑偶环呢,画图可以发现,如果删掉一条奇环偶环的公共边,那么原来的偶环就会形成新的奇环。
3:有多个奇环,那么所有返祖边都是不行的,那一条边一定要是所有奇环的公共边,同样的,也不能在偶环上。
所以dfs序搞一搞就出来了。
这题卡PE,有自环重边,图不一定连通,注意下。
code:

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>using namespace std;struct node{    int x,y,d,next;}a[2000010];int len=0,last[1000010];int n,m;bool vis[1000010];int p[1000010],fa[1000010];int dep[1000010],tr[1000010][2];bool is_tr[1000010];int num=0,tmp;int ans[1000010],NUM=0,la[1000010],ys[1000010],z=0;void ins(int x,int y,int d){    a[++len].x=x;a[len].y=y;a[len].d=d;    a[len].next=last[x];last[x]=len;}int lowbit(int x){return x&(-x);}void change(int x,int k,int rt){    for(int i=x;i<=n;i+=lowbit(i))        tr[i][rt]+=k;}int get(int x,int rt){    int ans=0;    for(int i=x;i>=1;i-=lowbit(i))        ans+=tr[i][rt];    return ans;}void dfs(int x,int FA,int from){    vis[x]=true;fa[x]=FA;p[x]=from;    ys[x]=la[x]=++z;    is_tr[from]=true;    dep[x]=dep[FA]+1;    for(int i=last[x];i;i=a[i].next)    {        int y=a[i].y;        if(is_tr[a[i].d]) continue;        if(!vis[y]) dfs(y,x,a[i].d),la[x]=la[y];        else        {            if(dep[x]<dep[y]) continue;            if((dep[x]-dep[y])%2==1) change(ys[y],-1,0),change(ys[x],1,0);            else            {                num++;tmp=a[i].d;                if(x!=y) change(ys[y],-1,1),change(ys[x],1,1);            }        }    }}int getsum(int x,int rt){return get(la[x],rt)-get(ys[x]-1,rt);}bool cmp(int a,int b){return a<b;}int main(){    scanf("%d %d",&n,&m);    for(int i=1;i<=m;i++)    {        int x,y;scanf("%d %d",&x,&y);        ins(x,y,i);        if(x!=y) ins(y,x,i);    }    memset(vis,false,sizeof(vis));    memset(is_tr,false,sizeof(is_tr));    for(int i=1;i<=n;i++) if(!vis[i]) dfs(i,0,0);    if(num==0)    {        printf("%d\n",m);        for(int i=1;i<m;i++) printf("%d ",i);        if(m!=0) printf("%d",m);        return 0;    }    for(int i=2;i<=n;i++)        if(getsum(i,1)==num&&getsum(i,0)==0) ans[++NUM]=p[i];    if(num==1) ans[++NUM]=tmp;    sort(ans+1,ans+NUM+1,cmp);    printf("%d\n",NUM);    for(int i=1;i<NUM;i++) printf("%d ",ans[i]);    if(NUM!=0) printf("%d",ans[NUM]);}
原创粉丝点击