题意:n点m条边的无向图,对每个i[i=1..n],询问删除点i后,是否能变为二分图.
1<=n,m<=1e5.
一个图为二分图等价于这个图不包含奇圈.然后无法求出所有奇圈并做交集.
cdq分治. 计算[l,r]的答案
如果[l,mid]的边已经包含奇圈 那么[mid+1,r]的答案显然为0.
否则递归到右半区间.
用并查集来维护是否有奇圈,
1<=n,m<=1e5.
一个图为二分图等价于这个图不包含奇圈.然后无法求出所有奇圈并做交集.
cdq分治. 计算[l,r]的答案
如果[l,mid]的边已经包含奇圈 那么[mid+1,r]的答案显然为0.
否则递归到右半区间.
用并查集来维护是否有奇圈,
为了要撤销操作,用按秩合并代替路径压缩 并且用栈来记录合并那些点. O(n*logn*logn).
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int T,n,m,res[N],sz[N],fa[N],stk[N],top=0,len[N];
int head[N],tot=0;
struct node{
int to,nxt;
}e[N];
void init(){
top=tot=0;
for(int i=1;i<=n;i++) fa[i]=i,sz[i]=res[i]=1,len[i]=0,head[i]=-1;
}
void link(int u,int v){
e[tot].to=v,e[tot].nxt=head[u];
head[u]=tot;
tot++;
}
bool check(int x,int y,int a,int b)
{
for(int i=x;i<=y;i++)
{
for(int j=head[i];j!=-1;j=e[j].nxt)
{
int u=i,v=e[j].to,du=0,dv=0;
if(v>=a&&v<=b) continue;
while(u!=fa[u]) du^=len[u],u=fa[u];
while(v!=fa[v]) dv^=len[v],v=fa[v];
if(u==v){
if(du==dv) return 0;
continue;
}
if(sz[u]<sz[v]) swap(u,v);
fa[v]=u;
len[v]=du^dv^1;
sz[u]+=sz[v];
stk[++top]=v;
}
}
return true;
}
void del(int u)
{
sz[fa[u]]-=sz[u];
fa[u]=u;
}
void cdq(int l,int r)
{
if(l==r) return;
int mid=l+r>>1,pre=top;
if(!check(l,mid,mid+1,r)) for(int i=mid+1;i<=r;i++) res[i]=0;
else cdq(mid+1,r);
while(top>pre) del(stk[top]),top--;
pre=top;
if(!check(mid+1,r,l,mid)) for(int i=l;i<=mid;i++) res[i]=0;
else cdq(l,mid);
while(top>pre) del(stk[top]),top--;
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init();
while(m--){
int u,v;
scanf("%d%d",&u,&v);
link(u,v),link(v,u);
}
cdq(1,n);
for(int i=1;i<=n;i++) printf("%d",res[i]);
printf("\n");
}
return 0;
}