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
l区间时,将
r
r
r区间置入S内,在图上添加
r
r
r区间的点与S内的点之间的边,每次加边都检查图中是否有奇环。
分治r区间时同理,但要注意将图还原到原状态。
如何判断有无奇环:
我们维护一个按子树大小合并的并查集,再保存每个点的层数奇偶性。
根节点默认为0,然后1010交替出现。
加入x到y的边有两种情况:
-
x和y在同一子树内:
此时判断x和y的颜色是否相同,若相同便连成奇环(自行画图),不相同就直接连边。 -
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);
}
}