可持久化并查集
可持久化并查集,luogu 3402
#include<bits/stdc++.h> #define rep(i,x,y) for(register int i=x;i<=y;i++) #define dec(i,x,y) for(register int i=x;i>=y;i--) #define ll long long using namespace std; const int N=301000; inline int read(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();} return x*f;} namespace zjc{ int n,m,idx,dep[N<<5],fa[N<<5],ls[N<<5],rs[N<<5],root[N<<5]; //一般主席树需要<<5大小,线段树需要<<2大小 inline void build(int &k,int l,int r){ k=++idx; if(l==r){fa[k]=l;return;}//下标是实际的父亲内映射 int mid=(l+r)>>1; build(ls[k],l,mid); build(rs[k],mid+1,r); } inline void merge(int &k,int kp,int l,int r,int p,int FA){ k=++idx; ls[k]=ls[kp],rs[k]=rs[kp]; if(l==r){fa[k]=FA;dep[k]=dep[kp];return;} int mid=(l+r)>>1; if(p<=mid) merge(ls[k],ls[kp],l,mid,p,FA); else merge(rs[k],rs[kp],mid+1,r,p,FA); } inline void change(int k,int l,int r,int p){ //本身这个操作的意义在于修改已经在merge过程中建立的节点的dep值 //所有不需要取地址 if(l==r){dep[k]++;return;} int mid=(l+r)>>1; if(p<=mid) change(ls[k],l,mid,p); else change(rs[k],mid+1,r,p); } //可持久化并查集下的自身实际上是某一编号对应的 //线段树下标!!!(实际上就是线段树中的k) //而在fa数组下存储的值实际上是真实的点, int query(int k,int l,int r,int p){ if(l==r) return k; int mid=(l+r)>>1; if(p<=mid) return query(ls[k],l,mid,p); else return query(rs[k],mid+1,r,p); } int find(int k,int p){ //k代表历史版本 //find代表在某一历史版本中找到父亲 //由于没有路径压缩实际上就是暴力跳 //find函数本身就是不加优化的并查集的操作 //query是由于将自身放入主席树维护导致增加的查找映射下标操作 //实际上最终存储的值仍然是正常父亲 int now=query(k,1,n,p); if(fa[now]==p) return now; return find(k,fa[now]); } void work(){ n=read(),m=read(); build(root[0],1,n); rep(i,1,m){ int opt,x,y; opt=read(),x=read(); if(opt==1){ y=read(); root[i]=root[i-1]; int xx=find(root[i],x),yy=find(root[i],y); if(fa[xx]!=fa[yy]){ if(dep[xx]>dep[yy]) swap(xx,yy); //将深度大的放在y上,完成fa[fa[xx]]=fa[yy](由于不是路径压缩,千万不敢写成fa[xx]=yy) //通俗理解为,我爸爸也是你爸爸的儿子,你是我叔叔 merge(root[i],root[i-1],1,n,fa[xx],fa[yy]); if(dep[xx]==dep[yy]) change(root[i],1,n,fa[yy]); //实质上就是按秩合并,但是具体操作在主席树上进行了实现 /*void init(int n){ for(int i=0;i<n;i++) fa[i]=i,rank[i]=0; } void unionn(int x,int y){ xx=find(x); yy=find(y); if(xx==yy) return; if(rank[x]<rank[y]) fa[x]=y;//秩小的父亲是秩大的,使得树的总高较小 else if(x==y){ fa[y]=x;rank[x]++;} else fa[y]=x; }*/ } } if(opt==2) root[i]=root[x]; if(opt==3){ y=read(); root[i]=root[i-1]; int xx=find(root[i],x),yy=find(root[i],y); if(fa[xx]==fa[yy]) puts("1"); else puts("0"); } }return; } } int main(){ zjc::work(); return 0; }