[BZOJ]3674: 可持久化并查集加强版
题目大意:n个点,三种操作:1.将点a点b连在一起;2.将当前状态退回到第k次操作时的状态;3.询问点a和点b是否连通。(n,操作数量<=200,000)
思路:可持久化并查集其实就是可持久化数组,我们用线段树代替数组,就只要打个可持久化线段树就可以了。路径压缩复杂度是均摊的不能可持久化,我们按秩合并,维护并查集每块的大小,连边把小的往大连,可以证明连出的最大深度为logn,总复杂度O(nlogn^2)。
#include<cstdio> #include<algorithm> using namespace std; inline int read() { int x;char c; while((c=getchar())<'0'||c>'9'); for(x=c-'0';(c=getchar())>='0'&&c<='9';)x=(x<<3)+(x<<1)+c-'0'; return x; } #define MN 200000 #define ND 8000000 struct data{int f,s;}; struct node{int l,r;data x;}t[ND+5]; int n,rt[MN+5],tn,p; int build(int l,int r) { int p=++tn,mid=l+r>>1; if(l==r)t[p].x=(data){l,1}; else t[p].l=build(l,mid),t[p].r=build(mid+1,r); return p; } data query(int k,int x) { for(int l=1,r=n;;) { if(l==r)return t[k].x; int mid=l+r>>1; if(x>mid)k=t[k].r,l=mid+1; else k=t[k].l,r=mid; } } int change(int k,int l,int r,int x,data d) { int p=++tn,mid=l+r>>1; if(l==r)t[p].x=d; else if(x>mid)t[p].l=t[k].l,t[p].r=change(t[k].r,mid+1,r,x,d); else t[p].l=change(t[k].l,l,mid,x,d),t[p].r=t[k].r; return p; } data gf(int k) { data d=query(rt[p],k); return d.f==k?d:gf(d.f); } int main() { int m,x,y,l=0;data a,b; n=read();m=read(); rt[0]=build(1,n); for(p=1;p<=m;++p) { rt[p]=rt[p-1];x=read();y=read()^l; if(x==1)if((a=gf(y)).f!=(b=gf(read()^l)).f) { if(a.s>b.s)swap(a,b); rt[p]=change(rt[p],1,n,a.f,(data){b.f,a.s}); rt[p]=change(rt[p],1,n,b.f,(data){b.f,a.s+b.s}); } if(x==2)rt[p]=rt[y]; if(x==3)printf("%d\n",l=(gf(y).f==gf(read()).f)); } }