题意:给出n个点,m条边,每条边有一个出现时间和一个消失时间,问从0-T的时间内,每个时间点的图是否是二分图
题解:
分治+并查集(按秩合并+带权)
众所周知的结论:二分图一定不存在奇环,存在奇环的图一定不是二分图
对时间分治,维护\([l,r]\)时间内存在的边,对每条边讨论:
1、当前边的起止时间刚好与\([l,r]\)相等,那么判断这条边所在的连通块是否构成奇环(让结点带权,记一下到根要经过奇数条边还是偶数条边即可),若成奇环,则\([l,r]\)时间内都为"No",否则这条边加入图中
2、若终止时间小于等于mid,则划分到左区间
3、若起始时间大于mid,则划分到右区间
4、否则两个区间一起丢
当l==r时,则输出"Yes"
注意:回溯时并查集要恢复到进入前的状态
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define ll long long
#define N 100010
using namespace std;
int n,m,T,top;
int stk[N],fa[N],dis[N],rank[N];
struct Node {int x,y,s,t;};
vector<Node> ve;
int gi() {
int x=0,o=1; char ch=getchar();
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') o=-1,ch=getchar();
while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
return o*x;
}
int find(int x) {
return x==fa[x]?x:find(fa[x]);
}
int get_dis(int x) {
int ret=0;
while(x!=fa[x]) ret^=dis[x],x=fa[x];
return ret;
}
void unite(int x, int y, int z) {
int xx=find(x),yy=find(y);
if(xx==yy) return;
if(rank[yy]>rank[xx]) swap(xx,yy);
if(rank[yy]==rank[xx]) rank[xx]++,stk[++top]=-xx;
fa[yy]=xx,dis[yy]=z,stk[++top]=yy;
}
void restore(int now) {
while(top>now) {
if(stk[top]<0) rank[-stk[top]]--;
else fa[stk[top]]=stk[top],dis[stk[top]]=0;
top--;
}
}
void bs(int l, int r, vector<Node> &e) {
int mid=(l+r)>>1,now=top,sz=e.size();
vector<Node> vl,vr;
for(int i=0; i<sz; i++) {
Node u=e[i];
int x=u.x,y=u.y;
if(l==u.s && r==u.t) {
int xx=find(x);
int yy=find(y);
int zz=get_dis(x)^get_dis(y)^1;
if(xx!=yy) unite(x,y,zz);
else if(zz&1) {
for(int j=l; j<=r; j++) puts("No");
restore(now);
return;
}
}
else if(u.t<=mid) vl.push_back(u);
else if(u.s>mid) vr.push_back(u);
else vl.push_back((Node){x,y,u.s,mid}),vr.push_back((Node){x,y,mid+1,u.t});
}
if(l==r) puts("Yes");
else bs(l,mid,vl),bs(mid+1,r,vr);
restore(now);
}
int main() {
n=gi(),m=gi(),T=gi();
for(int i=1; i<=n; i++) fa[i]=i;
for(int i=1; i<=m; i++) {
int x=gi(),y=gi(),s=gi()+1,t=gi();
if(s>t) continue;
ve.push_back((Node){x,y,s,t});
}
bs(1,T,ve);
return 0;
}