版权声明:欢迎随便转载。 https://blog.csdn.net/a1214034447/article/details/88081197
题目链接:https://www.luogu.org/problemnew/show/P3402
解题思路:
可持久化并查集也就是可持续化线段树 + 并查集 == 主席树 + 并查集
像我们平常做的并查集都是路径压缩,但因为要保证可持续化,所以信息不能改变,所以我们采用启发式合并来合并集合。
启发式合并的树最高深度不会超过log(n)+1。因为深度为2的树需要两个点,那么深度为3的需要两个深度为2的也就是2*2==4个点。所以2^n次方个点最深可以到log(n)+1。
所以主席树每个点就需要维护他的父亲和它为根的树的深度。而且只需要维护区间为1的节点就可以了。
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define lson l,mid,s[rt].ls
#define rson mid+1,r,s[rt].rs
using namespace std;
typedef long long ll;
const int mx = 2e5 + 10;
const int mod = 1e9+7;
const ll INF = -1e16;
int n,m,siz,root[mx];
struct node
{
int f,dep;
int ls,rs;
}s[mx*30];
void build(int l,int r,int& rt)
{
rt = siz++;
if(l==r){
s[rt].f = l;
return ;
}
int mid = (l+r)>>1;
build(lson);build(rson);
}
node query(int l,int r,int rt,int p)
{
if(l==r) return s[rt];
int mid = (l+r) >> 1;
if(p<=mid) return query(lson,p);
return query(rson,p);
}
node find(int rt,int x)
{
while(1){
node now = query(1,n,rt,x);
if(now.f == x) return now;
x = now.f;
}
}
void merge(int l,int r,int& rt,int x,int p,int fa)
{
rt = siz++;
s[rt] = s[x];
if(l==r){
s[rt].f = fa;
return ;
}
int mid = (l+r)>>1;
if(p<=mid) merge(lson,s[x].ls,p,fa);
else merge(rson,s[x].rs,p,fa);
}
void add(int l,int r,int rt,int p)
{
if(l==r){
s[rt].dep++;
return ;
}
int mid = (l+r)>>1;
if(p<=mid) add(lson,p);
else add(rson,p);
}
void update(int a,int b,int i)
{
node vx = find(root[i-1],a);
node vy = find(root[i-1],b);
if(vx.f==vy.f) return ;
if(vx.dep>vy.dep) swap(vx,vy);
merge(1,n,root[i],root[i-1],vx.f,vy.f);
if(vx.dep==vy.dep) add(1,n,root[i],vy.f);
}
int main()
{
scanf("%d%d",&n,&m);
build(1,n,root[0]);
for(int i=1;i<=m;i++){
int c,a,b;
scanf("%d",&c);
if(c==1){
scanf("%d%d",&a,&b);
root[i] = root[i-1];
update(a,b,i);
}else if(c==2){
scanf("%d",&a);
root[i] = root[a];
}else{
root[i] = root[i-1];
scanf("%d%d",&a,&b);
puts(find(root[i],a).f==find(root[i],b).f?"1":"0");
}
}
return 0;
}