Description
n个集合 m个操作
操作:
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)
3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
Solution
用主席树维护一个可持久化的数组来进行并查集操作
并查集按秩合并
一开始没有路径压缩,果然被加强版卡掉了,然后改了改代码,RE了几发(于是AC率又下降了1%)
发现数组开小了…!@#¥%…&*
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #define MAXN 200005 using namespace std; int n,m,tot=0; int rt[MAXN],ls[MAXN*50],rs[MAXN*50],f[MAXN*50],h[MAXN*50]; int read() { int x=0,f=1;char c=getchar(); while(c<'0'||c>'9'){ if(c=='-')f=-1;c=getchar(); } while(c>='0'&&c<='9'){ x=x*10+c-'0';c=getchar(); } return x*f; } void build(int &idx,int l,int r) { tot++,idx=tot; if(l==r){f[idx]=l,h[idx]=0;return;} int mid=(l+r)>>1; build(ls[idx],l,mid); build(rs[idx],mid+1,r); } int query(int idx,int l,int r,int x) { if(l==r)return idx; int mid=(l+r)>>1; if(x<=mid)return query(ls[idx],l,mid,x); else return query(rs[idx],mid+1,r,x); } void insert(int &idx,int last,int l,int r,int pos,int x) { tot++,idx=tot; ls[idx]=ls[last],rs[idx]=rs[last]; if(l==r){f[idx]=x;h[idx]=h[last];return;} int mid=(l+r)>>1; if(pos<=mid)insert(ls[idx],ls[last],l,mid,pos,x); else insert(rs[idx],rs[last],mid+1,r,pos,x); } int find(int &idx,int last,int x) { int t=query(idx,1,n,x); if(f[t]==x)return t; int p=find(idx,last,f[t]); insert(idx,last,1,n,x,f[p]); return p; } int main() { n=read(),m=read(); build(rt[0],1,n); int opt,a,b,k,x,y,key=0; for(int i=1;i<=m;i++) { opt=read(); switch(opt) { case 1: a=read(),b=read(); a^=key,b^=key; rt[i]=rt[i-1]; x=find(rt[i],rt[i-1],a),y=find(rt[i],rt[i-1],b); if(f[x]==f[y])continue; if(h[x]==h[y])h[x]++; if(h[x]<h[y])swap(x,y); insert(rt[i],rt[i-1],1,n,f[y],f[x]); break; case 2: k=read(); k^=key; rt[i]=rt[k]; break; case 3: a=read(),b=read(); a^=key,b^=key; rt[i]=rt[i-1]; x=find(rt[i],rt[i-1],a),y=find(rt[i],rt[i-1],b); if(f[x]==f[y]){printf("1 ");key=1;} else {printf("0 ");key=0;} break; } } return 0; }