题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=5354
题意:问删除每个点后能否构成二分图。
做法:
考虑分治,solve(l,r)是用来求区间l到r的答案,那么可以把l到mid的点都加入并查集,如果存在奇环了,那么mid+1到r的答都
是0,如果没有奇环,继续solve(mid+1,r)。 然后用相同的做法处理l到mid的答案。
并查集需要还原,所以用启发式合并,用一个栈来记录需要还原的数据。这样复杂度是nlognlogn。
#include<bits/stdc++.h> using namespace std; const int N=100005; int base[N],vec[2*N],pre[2*N],tot; void link(int x,int y) { vec[++tot]=y; pre[tot]=base[x]; base[x]=tot; } int fa[N],sz[N],len[N]; int top,stk[N]; bool check(int l,int r,int x,int y) { for (int u=l;u<=r;u++) { for (int now=base[u];now;now=pre[now]) { int v=vec[now]; if (v>=x&&v<=y) continue; int su=0,sv=0,fu=u,fv=v; while(fu!=fa[fu]) su^=len[fu],fu=fa[fu]; while(fv!=fa[fv]) sv^=len[fv],fv=fa[fv]; if (fu==fv) { if (su==sv) return 0; continue; } if (sz[fu]<sz[fv]) swap(fu,fv); stk[++top]=fv; fa[fv]=fu; sz[fu]+=sz[fv]; len[fv]=su^sv^1; } } return 1; } void load(int u) { sz[fa[u]]-=sz[u]; fa[u]=u; } int ans[N]; void solve(int l,int r) { if (l==r) { ans[l]=1; return; } int pretop=top; int mid=(l+r)/2; if (!check(l,mid,mid+1,r)) for (int i=mid+1;i<=r;i++) ans[i]=0; else solve(mid+1,r); while(top>pretop) load(stk[top]),top--; if (!check(mid+1,r,l,mid)) for (int i=l;i<=mid;i++) ans[i]=0; else solve(l,mid); while(top>pretop) load(stk[top]),top--; } int main() { int t; scanf("%d",&t); int n,m; while(t--) { scanf("%d%d",&n,&m); while(m--) { int x,y; scanf("%d%d",&x,&y); link(x,y); link(y,x); } for (int i=1;i<=n;i++) fa[i]=i,sz[i]=1; solve(1,n); for (int i=1;i<=n;i++) printf("%d",ans[i]); printf("\n"); tot=0; for (int i=1;i<=n;i++) base[i]=0; } }