BZOJ 3673: 可持久化并查集(可持久化并查集+启发式合并)
http://www.lydsy.com/JudgeOnline/problem.php?id=3673

思路:
可持久化数组可以用可持久化线段树来实现,并查集的查询操作和原来的一般并查集操作是差不多的,只不过是在线段树上操作。需要注意的是并查集的合并,需要按秩来进行启发式合并。
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 2*10000+5;
int n,m,tot;
int t[maxn*50],h[maxn*50];
struct node
{
int l,r,fa;
}a[maxn*50];
int build(int l, int r)
{
int root = ++tot;
if(l==r) {a[root].fa = l;return root;}
int mid = (l+r)>>1;
a[root].l = build(l,mid);
a[root].r = build(mid+1,r);
return root;
}
int query(int root, int x, int l, int r)
{
if(l==r) return root;
int mid = (l+r)>>1;
if(x<=mid) return query(a[root].l,x,l,mid);
else return query(a[root].r,x,mid+1,r);
}
int finds(int root, int x)
{
int p = query(root,x,1,n);
if(x == a[p].fa) return p;
else return finds(root,a[p].fa);
}
int modify(int pre, int x, int y, int l, int r)
{
int root = ++tot;
if(l==r)
{
a[root].fa = y;
h[root] = h[pre]; //这个不能忘
return root;
}
a[root].l = a[pre].l, a[root].r = a[pre].r;
int mid = (l+r)>>1;
if(x<=mid) a[root].l = modify(a[pre].l, x, y, l, mid);
else a[root].r = modify(a[pre].r, x, y, mid+1, r);
return root;
}
void update(int root, int x, int l, int r)
{
if(l==r) {h[root]++;return;}
int mid = (l+r)>>1;
if(x<=mid) update(a[root].l,x, l, mid);
else update(a[root].r,x, mid+1, r);
}
void unions(int x, int y, int i)
{
if(h[x]>h[y]) swap(x,y);
t[i] = modify(t[i-1],a[x].fa,a[y].fa,1,n); //因为上面的交换,所以这里用fa
if(h[x]==h[y]) update(t[i],a[y].fa,1,n); //如果深度相等,则插入后深度会+1
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
tot = 0;
t[0] = build(1,n);
for(int i=1;i<=m;i++)
{
int op;
scanf("%d",&op);
if(op==1)
{
int aa,bb;
scanf("%d%d",&aa,&bb);
t[i] = t[i-1];
int x = finds(t[i],aa);
int y = finds(t[i],bb);
if(a[x].fa != a[y].fa) unions(x,y,i);
}
if(op==2)
{
int aa;
scanf("%d",&aa);
t[i] = t[aa];
}
if(op==3)
{
int aa,bb;
scanf("%d%d",&aa,&bb);
t[i] = t[i-1];
int x = finds(t[i],aa);
int y = finds(t[i],bb);
if(a[x].fa == a[y].fa) puts("1");
else puts("0");
}
}
return 0;
}

浙公网安备 33010602011771号