题意:给你三种操作
1 a b 合并a,b所在集合
2 k 回到第k次操作之后的状态(查询算作操作)3 a b 询问a,b是否属于同一集合,是则输出1否则输出0
题解:通过主席树维护非路径压缩的并查集,并查集通过启发式合并保证树深度不超过logn,总复杂度O(mlognlogn)。
AC代码:
#include<stdio.h>
#include<algorithm>
#define N 400005
using namespace std;
int tree[N*23],lchild[N*23],rchild[N*23],root[N],Rank[N*23];
int n,m,tot;
void build(int L,int R,int root)
{
if(L==R)
{
tree[root]=L;
return ;
}
int mid=L+R>>1;
build(L,mid,lchild[root]=++tot);
build(mid+1,R,rchild[root]=++tot);
}
void update(int last,int cur,int L,int R,int pos,int k,int flag)
{
tree[cur]=tree[last];
Rank[cur]=Rank[last];
lchild[cur]=lchild[last];
rchild[cur]=rchild[last];
if(L==R)
{
if(flag==0)tree[cur]=k;
Rank[cur]+=flag;
return ;
}
int mid=L+R>>1;
if(pos<=mid)update(lchild[last],lchild[cur]=++tot,L,mid,pos,k,flag);
else update(rchild[last],rchild[cur]=++tot,mid+1,R,pos,k,flag);
}
int query(int x,int L,int R,int root)
{
if(L==R)return root;
int mid=L+R>>1;
if(x<=mid)return query(x,L,mid,lchild[root]);
else return query(x,mid+1,R,rchild[root]);
}
int find(int x,int root)
{
int fa=query(x,1,n,root);
if(tree[fa]!=x)return find(tree[fa],root);
return fa;
}
int main()
{
scanf("%d%d",&n,&m);
build(1,n,root[0]);
int last=0;
for(int i=1;i<=m;i++)
{
int op;
scanf("%d",&op);
if(op==1)
{
int u,v;
scanf("%d%d",&u,&v);
u^=last;v^=last;
root[i]=root[i-1];
u=find(u,root[i-1]);
v=find(v,root[i-1]);
if(tree[u]==tree[v])continue;
if(Rank[u]<Rank[v])swap(u,v);
update(root[i-1],root[i]=++tot,1,n,tree[v],tree[u],0);
if(Rank[u]==Rank[v])
{
int now=++tot;
update(root[i],now,1,n,tree[u],tree[u],1);
root[i]=now;
}
}
if(op==2)
{
int k;
scanf("%d",&k);
k^=last;
root[i]=root[k];
}
if(op==3)
{
int u,v;
scanf("%d%d",&u,&v);
u^=last;v^=last;
root[i]=root[i-1];
u=find(u,root[i-1]);
v=find(v,root[i-1]);
printf("%d\n",tree[u]==tree[v]);
last=tree[u]==tree[v];
}
}
}