BZOJ 4025: 二分图
Description
神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。
Input
输入数据的第一行是三个整数n,m,T。
第2行到第m+1行,每行4个整数u,v,start,end。第i+1行的四个整数表示第i条边连接u,v两个点,这条边在start时刻出现,在第end时刻消失。
Output
输出包含T行。在第i行中,如果第i时间段内这个图是二分图,那么输出“Yes”,否则输出“No”,不含引号。
Sample Input
3 3 3
1 2 0 2
2 3 0 3
1 3 1 2
1 2 0 2
2 3 0 3
1 3 1 2
Sample Output
Yes
No
Yes
No
Yes
HINT
样例说明:
0时刻,出现两条边1-2和2-3。
第1时间段内,这个图是二分图,输出Yes。
1时刻,出现一条边1-3。
第2时间段内,这个图不是二分图,输出No。
2时刻,1-2和1-3两条边消失。
第3时间段内,只有一条边2-3,这个图是二分图,输出Yes。
数据范围:
n<=100000,m<=200000,T<=100000,1<=u,v<=n,0<=start<=end<=T。
题解:
这个题目,我们考虑套路CDQ 图分治,以边出现的时间L,R分治,每次我们算区间(L~R)十分可能。
对于时间段L~R,我们先把时间等于L~R的边加入图中,如果出现了基环,那么这一段时间都是不可能的,否则,我们将剩下的边按照<mid,>mid和在中间分类,把在中间的边拆成两个进行下一次递归,如果递归到最后一层都没有问题就是好的。
然后,我们用并查集维护这个图的联通性,带一个权,维护这个点到到父亲的边数的基偶性,这样就可以维护出图中十分存在基环,具体,如果在一个并查集中,那么环的基偶性就是get(x)^get(y)^1,get(x)是x到首元素的基偶性,因为异或就相当于加,所以画一个图就理解了。对于合并,就直接讲首按照dep合并,这样是logn的,如果fa[fx]=fy,就改一下fx的权,改get(x)^get(y)^1,get(x),这个是等效的,证明的话自己思考一下,还是比较有趣的。
代码:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #include <cmath> #include <iostream> #include <vector> #define MAXN 201000 using namespace std; int fa[MAXN],rak[MAXN],d[MAXN]; int s1[MAXN*2],s2[MAXN],ans[MAXN],top=0; struct edge{ int x,y,s,e; }; vector<edge> S; int n,m,t; int find(int x){ while(x!=fa[x]) x=fa[x]; return x; } int getdis(int x){ int ans=0; while(x!=fa[x]) ans^=d[x],x=fa[x]; return ans; } void mearge(int x,int y,int c){ if(rak[x]>rak[y]){ s1[++top]=y;s2[top]=1; fa[y]=x,d[y]=c; } else if(rak[y]>rak[x]){ s1[++top]=x;s2[top]=1; fa[x]=y;d[x]=c; } else{ rak[x]++;fa[y]=x; s1[++top]=y;s2[++top]=0; fa[y]=x,d[y]=c; } } void fenzhi(int l,int r,vector<edge> hh){ int now=top,mid=(l+r)/2; vector<edge> ll,rr; for(int i=0,len=hh.size();i<len;i++){ edge a=hh[i]; int x=a.x,y=a.y; if(a.s==l&&a.e==r){ int fx=find(x),fy=find(y),c=getdis(x)^getdis(y)^1; if(fx!=fy) mearge(fx,fy,c); else if(c&1){ for(int i=l;i<=r;i++) ans[i]=0; while(now!=top){ if(s2[top]==0) rak[fa[s1[top]]]--;// fa[s1[top]]=s1[top];d[s1[top]]=0;top--; } return; } } else if(a.e<=mid) ll.push_back(a); else if(a.s>mid) rr.push_back(a); else{ edge b=a; b.e=mid;ll.push_back(b); b=a;b.s=mid+1; rr.push_back(b); } } if(l==r) ans[l]=1; else fenzhi(l,mid,ll),fenzhi(mid+1,r,rr); while(now!=top){ if(s2[top]==0) rak[fa[s1[top]]]--;// fa[s1[top]]=s1[top];d[s1[top]]=0;top--; } } int main() { scanf("%d%d%d",&n,&m,&t); for(int i=1;i<=m;i++){ int x,y,s,t;scanf("%d%d%d%d",&x,&y,&s,&t);s++; if(s<=t) S.push_back((edge){x,y,s,t}); } for(int i=1;i<=n;i++) fa[i]=i,rak[i]=0,d[i]=0; for(int i=1;i<=n;i++) ans[i]=1; fenzhi(1,t,S); for(int i=1;i<=t;i++){ if(ans[i]) printf("Yes\n"); else printf("No\n"); } return 0; }