luogu P5787 二分图 /【模板】线段树分治
题面传送门
又是一个自己yy出来的数据结构。
考虑在时间轴上建立线段树。
模仿左偏树那题,把修改拆成\(logn\)个询问。
对于每个节点,用扩展域并查集来维护有没有奇环。然后如果这个区间有奇环那么就直接输出。
但是还有一个问题退出当前节点时要撤销这个点的贡献。
那么可以上按秩合并可持久化并查集即可。
代码实现:
#include<cstdio>
#define l(x) x<<1
#define r(x) x<<1|1
#include<vector>
using namespace std;
int n,m,k,x,y,xs,ys,z,head,fa[200039],w[200039],un,wn,flag;
struct yyy{int x,y,flag;}s[200039];
struct ques{int x,y;}tmp;
vector<ques> f[400039];
inline void get(int x,int y,int xs,int ys,int l,int r,int now){
if(x<=l&&r<=y){f[now].push_back((ques){xs,ys});return;}
int m=l+r>>1;
if(x<=m) get(x,y,xs,ys,l,m,l(now));
if(y>m) get(x,y,xs,ys,m+1,r,r(now));
}
inline int find(int x){return fa[x]==x?x:find(fa[x]);}
inline void swap(int &x,int &y){x^=y^=x^=y;}
inline void merge(int x,int y){
un=find(x);wn=find(y);
if(un!=wn){
if(w[un]<w[wn])swap(un,wn);
s[++head]=(yyy){un,wn,w[un]==w[wn]};
fa[wn]=un;
if(w[wn]==w[un]) w[un]++;
}
}
inline void del(){fa[s[head].y]=s[head].y;w[s[head].x]-=s[head].flag;head--;}
inline void find(int l,int r,int now){
int lasthead=head;flag=0;
for(int i=0;i<f[now].size();i++){
tmp=f[now][i];
un=find(tmp.x);wn=find(tmp.y);
if(un==wn){
for(int j=l;j<=r;j++) printf("No\n");
flag=1;break;
}
merge(tmp.x,tmp.y+n);merge(tmp.y,tmp.x+n);
}
if(l==r&&!flag) printf("Yes\n");
else if(!flag){
int m=l+r>>1;
find(l,m,l(now));find(m+1,r,r(now));
}
while(head!=lasthead) del();
return;
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=2*n;i++) fa[i]=i,w[i]=1;
for(i=1;i<=m;i++) scanf("%d%d%d%d",&xs,&ys,&x,&y),get(x+1,y,xs,ys,1,k,1);
find(1,k,1);
}