noip2019集训测试赛(四)C.graph

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/tylon2006/article/details/100556029

Description

给定一张 n 个点 m 条边的无向图, 问删去每个点后, 原图是不是二分图.


Input

输入文件包含多组数据, 文件开头给定数据组数 T .

对于每组数据:

第一行两个整数 n,m .

接下来 m 行, 每行两个整数 u,v , 表示图中有边 (u,v) , 保证 u≠v .


Output

对于每组数据, 输出一行长度为 n 的字符串 s , si=0 表示删除第 i 个点后原图不是二分图, si=1 表示删除后是二分图。


Solution

神奇CDQ分治。

首先要知道二分图的定义:没有奇环的图

我们分治n个点,以不停加入边的方式判断去掉某个点是否可行。
设置一个空集S,和一个图G(初始为空),每次分治 l l 区间时,将 r r 区间置入S内,在图上添加 r r 区间的点与S内的点之间的边,每次加边都检查图中是否有奇环。

分治r区间时同理,但要注意将图还原到原状态。

如何判断有无奇环:

我们维护一个按子树大小合并的并查集,再保存每个点的层数奇偶性。
根节点默认为0,然后1010交替出现。

加入x到y的边有两种情况:

  1. x和y在同一子树内:
    此时判断x和y的颜色是否相同,若相同便连成奇环(自行画图),不相同就直接连边。

  2. x和y在同一子树内:
    直接连边。

注意按秩合并不能路径压缩。


Code

#include<bits/stdc++.h>
using namespace std;
int f[100010],to[200010],head[100010],nxt[200010],siz[100010],col[100010],top,cnt,n,m;
char ans[100010];
struct data{
    int u,v,fau,fav,colu,colv,sizu,sizv;
}stk[100010],tmp;
void add(int x,int y){
    ++cnt;
    to[cnt]=y;
    nxt[cnt]=head[x];
    head[x]=cnt; 
}
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,int ll,int rr){//加边
    for(int i=l;i<=r;i++)
    for(int j=head[i];j;j=nxt[j]){
        if(ll<=to[j]&&to[j]<=rr) continue;
        if(!merge(i,to[j])) 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){
        ans[l]=flag+'0';
        return;
    }
    int last=top,mid=(l+r)>>1;
    bool now=flag&&work(mid+1,r,l,mid);
    solve(l,mid,now); init(last);
    now=flag&&work(l,mid,mid+1,r);
    solve(mid+1,r,now); init(last);
}
int main(){
    int x,y,t;
    scanf("%d",&t);
    while(t--){
        top=cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            head[i]=col[i]=0;
            siz[i]=1;
            f[i]=i;
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d",&x,&y);
            add(x,y),add(y,x);
        }
        ans[n+1]=0; //只是为了不多输出
        solve(1,n,1);
        printf("%s\n",ans+1);   
    }
}

猜你喜欢

转载自blog.csdn.net/tylon2006/article/details/100556029