luogu传送门
题目描述
给定 n 个集合,第 i个集合内初始状态下只有一个数,为 i。
有 m 次操作。操作分为 3 种:
1 a b 合并 a,b 所在集合;
2 k 回到第 k 次操作(执行三种操作中的任意一种都记为一次操作)之后的状态;
3 a b 询问 a,b 是否属于同一集合,如果是则输出 1 ,否则输出 0。
输入格式
第一行两个整数,n,m。
接下来 m 行,每行先输入一个数 opt。若 opt=2则再输入一个整数 k,否则再输入两个整数 a,b,描述一次操作。
输出格式
对每个操作 3,输出一行一个整数表示答案。
话说没做之前一直以为又是什么新算法,做了发现其实还是主席树的运用吧,
首先,我们要会主席树和并查集;
并查集中有几种合并方式:
一种是直接暴力连父亲显然不可能考你这个;
一种是路径压缩的合并(这个在普通并查集中很常用,但是好像无法在可持久化并查集中用,蒟蒻表示不知道为啥);
还有一种是按秩合并,也就是可持久化并查集中常用的合并方式!其实也就是一种类似于启发式合并的方式,每一次合并时选择一个深度小的点向深度大的合并。这样就可以保证并查集的高度不会增长的太快,保证高度尽量均衡。
可持久化并查集其实就是指的用可持久化数组维护并查集中的Fa与按秩合并所需要的dep
所谓可持久化并查集,可以进行的操作就只有几个:
回到历史版本
合并两个集合
查询节点所在集合的祖先,
当然,因此也可以判断是否在同一个集合中!对于1操作,我们可以很轻松的利用主席树实现。就直接把当前版本的根节点定为第k个版本的根节点就行了!2操作便是记住按秩合并。3操作直接按并查集那样查就是了。
直接上代码吧!
#include<bits/stdc++.h>
using namespace std;
const int N=3e6;
int n,m,fa[N],L[N],R[N],dep[N],cnt,rt[N],saber,x,y;
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-'0',ch=getchar();
return f*x;
}
int build(int l,int r)
{
cnt++;
int u=cnt;
if(l==r){fa[u]=l;return u;}
int mid=(l+r)>>1;
L[u]=build(l,mid),R[u]=build(mid+1,r);
return u;
}
int query(int Rt,int l,int r,int x)
{
if(l==r) return Rt;
int mid=(l+r)>>1;
if(x<=mid) return query(L[Rt],l,mid,x);
else return query(R[Rt],mid+1,r,x);
}
int Get(int Rt,int x)
{
int pos=query(Rt,1,n,x);
if(fa[pos]==x) return pos;
return Get(Rt,fa[pos]);
}
int merge(int l,int r,int pre,int f1,int f2)
{
cnt++;
int u=cnt;
L[u]=L[pre],R[u]=R[pre];
if(l==r)
{
dep[u]=dep[pre];
fa[u]=f2;
return u;
}
int mid=(l+r)>>1;
if(f1<=mid) L[u]=merge(l,mid,L[pre],f1,f2);
else R[u]=merge(mid+1,r,R[pre],f1,f2);
return u;
}
void updatdep(int Rt,int l,int r,int f)
{
if(l==r){dep[Rt]++;return;}
int mid=(l+r)>>1;
if(f<=mid) updatdep(L[Rt],l,mid,f);
else updatdep(R[Rt],mid+1,r,f);
}
int main()
{
n=Read(),m=Read();
rt[0]=build(1,n);
for(int i=1;i<=m;i++)
{
saber=Read();
if(saber==2) x=Read(),rt[i]=rt[x];
if(saber==1)
{
x=Read(),y=Read();
rt[i]=rt[i-1];
int f1=Get(rt[i-1],x),f2=Get(rt[i-1],y);//这里返回的并不是它真正祖先,只是为了减少Get函数调用。
if(f1!=f2)
{
if(dep[f1]>dep[f2]) swap(f1,f2);
rt[i]=merge(1,n,rt[i-1],fa[f1],fa[f2]);
if(dep[f1]==dep[f2]) updatdep(rt[i],1,n,fa[f2]);
}
}
if(saber==3)
{
x=Read(),y=Read();
rt[i]=rt[i-1];
int f1=fa[Get(rt[i-1],x)],f2=fa[Get(rt[i-1],y)];
if(f1==f2) printf("1\n");
else printf("0\n");
}
}
return 0;
}