[模板] 可持久化并查集

## 题目描述

n个集合 m个操作

操作:

  • 1 a b 合并a,b所在集合

  • 2 k 回到第k次操作之后的状态(查询算作操作)

  • 3 a b 询问a,b是否属于同一集合,是则输出1否则输出0

输入样例#1:

5 6
1 1 2
3 1 2
2 0
3 1 2
2 1
3 1 2

输出样例#1:

1
0
1

说明

\[1≤n≤10^5,1≤m≤2*10^5 \]

By zky 出题人大神犇

Solution

最近几天我看到2018NOI day1 T1 可以用可持化并查集做,我最喜欢可持久化数据结构了,于是就去学了一波。
可持久化并查集利用的是可持久化数组+按秩合并并查集(好像不可以路路径压缩),可持久化数组不会的话可以看我的另一篇博客可持久化数组。我们用数组fa[]来存一个节点的父亲,而不是祖先。dep[]数组存的是当前并查集的深度,因为要按秩合并。剩下的在代码中解释,不要在意我的中文式打法。

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int root[201000],fa[10001000],tot,dep[10000010],n;
struct TREE
{
    int ln,rn;
}t[10010000];//主席树
void build(int &node,int l,int r)
{
    node=++tot;
    if(l==r) {fa[node]=l;dep[node]=1;return;}
    int mid=(l+r)/2;
    build(t[node].ln,l,mid);
    build(t[node].rn,mid+1,r);
}
int find(int node,int l,int r,int x)
{
    if(l==r) return node;
    int mid=(l+r)/2;
    if(x<=mid) return find(t[node].ln,l,mid,x);
    else return find(t[node].rn,mid+1,r,x);
}
int getfa(int node,int x)
{
    int p=find(node,1,n,x);
    if(fa[p]==x) return p;
    else return getfa(node,fa[p]);
}
void gai(int &node,int last,int l,int r,int x,int y)
{
    node=++tot;
    if(l==r) {fa[node]=y;dep[node]=dep[last];return;}
    int mid=(l+r)/2;
    t[node]=t[last];
    if(x<=mid) gai(t[node].ln,t[last].ln,l,mid,x,y);
    else gai(t[node].rn,t[last].rn,mid+1,r,x,y);
}
void xiu(int node,int l,int r,int x)
{
    if(l==r) {dep[node]++;return;}
    int mid=(l+r)/2;
    if(x<=mid) xiu(t[node].ln,l,mid,x);
    else xiu(t[node].rn,mid+1,r,x);
}
void merge(int x,int y,int i)//合并
{
    if(dep[x]>dep[y]) swap(x,y);//按秩合并
    gai(root[i],root[i-1],1,n,fa[x],fa[y]);
    if(dep[x]==dep[y]) xiu(root[i],1,n,fa[y]);//深度一样,修改父节点深度
}
int main()
{
    int m,opt,x,y;
    cin>>n>>m;
    build(root[0],1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&opt);
        root[i]=root[i-1];
        if(opt==1)
        {
            scanf("%d%d",&x,&y);
            x=getfa(root[i],x);y=getfa(root[i],y);
            if(fa[x]!=fa[y]) merge(x,y,i);
        }
        if(opt==2)
        scanf("%d",&x),root[i]=root[x];
        if(opt==3)
        {
            scanf("%d%d",&x,&y);
            x=getfa(root[i],x);y=getfa(root[i],y);
            if(fa[x]!=fa[y]) printf("0\n");
            else printf("1\n");
        }
    }
}

学完之后就差不多可以打2018NOI归程了。

posted @ 2018-07-27 10:28  k-z-j  阅读(215)  评论(0编辑  收藏  举报