Luogu3402 可持久化并查集
https://www.luogu.com.cn/problem/P3402
可持久化并查集
利用主席树的性质进行求解
注意弄清楚每个函数的返回值是主席树中的位置还是原序列中的位置(即在哪个集合)
\(O(n \log^2n)\)
C++ Code:
#include<bits/stdc++.h>
#define N 200005
using namespace std;
struct node
{
int l,r,f,ls,rs,dep;
//l,r表示该节点所覆盖的区间为[l,r]
//f表示该节点的父亲(序列中的位置),只有子节点才有(区间长度为1的序列,即1个数)
//ls,rs表示主席树中的左右儿子
//dep表示深度,按秩合并时使用
}s[N << 6];
int n,m,opt,x,y,rx,ry,fx,fy,cnt,pre;
int t[N];
int build(int l,int r)//建立初始的主席树
{
int rt=++cnt;
s[rt].l=l;
s[rt].r=r;
if (l==r)
{
s[rt].f=l;
s[rt].dep=1;
return rt;
}
int mid=(l+r) >> 1;
s[rt].ls=build(l,mid);
s[rt].rs=build(mid+1,r);
return rt;
}
int find_p(int g,int x)//查找x(序列中的位置)在g版本中的位置(主席树中的位置)
{
if (s[g].l==s[g].r)
return g;
int mid=(s[g].l+s[g].r) >> 1;
if (x<=mid)
return find_p(s[g].ls,x); else
return find_p(s[g].rs,x);
}
int find_f(int g,int x)//查找x(序列中的位置)在g版本中的父亲(主席树中的位置)
{
int fx=find_p(g,x);
if (s[fx].f==x)
return fx;
return find_f(g,s[fx].f);
}
void update(int g,int x)//因为要更新x的父亲和深度,先把这条链重新建出来
{
if (s[g].l==s[g].r)
return;
int mid=(s[g].l+s[g].r) >> 1;
if (x<=mid)
{
s[++cnt]=s[s[g].ls];
s[g].ls=cnt;
update(cnt,x);
} else
{
s[++cnt]=s[s[g].rs];
s[g].rs=cnt;
update(cnt,x);
}
}
void merge(int g,int x,int y)//合并x集合与y集合
{
int fx=find_f(g,x);//查找x的父亲
int fy=find_f(g,y);//查找y的父亲
if (s[fx].l==s[fy].l)//在同一集合,无需操作
return;
if (s[fx].dep>s[fy].dep)//按秩合并
swap(fx,fy);
update(g,s[fx].l);//拆出新链(可持久化)
fx=cnt;//根据update函数可知,cnt即为该数的位置
update(g,s[fy].l);
fy=cnt;
s[fx].f=s[fy].l;//合并
if (s[fx].dep==s[fy].dep)//更新dep
s[fy].dep++;
}
int main()
{
scanf("%d%d",&n,&m);
t[0]=build(1,n);
pre=t[0];
for (int i=1;i<=m;i++)
{
scanf("%d",&opt);
switch (opt)
{
case 1:
scanf("%d%d",&x,&y);
s[++cnt]=s[pre];
t[i]=cnt;
pre=cnt;
merge(t[i],x,y);
break;
case 2:
scanf("%d",&x);
s[++cnt]=s[t[x]];
t[i]=cnt;
pre=cnt;
break;
case 3:
scanf("%d%d",&x,&y);
s[++cnt]=s[pre];
t[i]=cnt;
pre=cnt;
fx=find_f(cnt,x);
fy=find_f(cnt,y);
if (s[fx].f==s[fy].f)
puts("1"); else
puts("0");
break;
}
}
return 0;
}