模板 - 左偏树 + 并查集

Posted inko

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了模板 - 左偏树 + 并查集相关的知识,希望对你有一定的参考价值。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int solve();

int main() {
#ifdef Yinku
    freopen("Yinku.in","r",stdin);
#endif // Yinku
    solve();
}

int n,m;

const int MAXN=100005;
int tot,v[MAXN],l[MAXN],r[MAXN],d[MAXN],f[MAXN];

//以siz为例代表并查集的信息,属于整棵左偏树
int siz[MAXN];

//这其实是一片左偏树-并查集森林,注意不要抄错Merge和_Merge
class Leftist_Tree {
    int _Merge(int x,int y) {
        //将两棵左偏树合并,返回他们的新根
        //当其中一棵是空树,直接返回另一棵
        if(!x||!y)
            return x+y;

        //此处符号决定是最小堆还是最大堆
        //这里是小于号就是最大堆,是大于号就是小根堆
        //维持x不比y小,相等则把编号小的那个作为根,相等其实也可以灵活处理
        if(v[x]>v[y]||(v[x]==v[y]&&x>y)){
            swap(x,y);
        }

        r[x]=_Merge(r[x],y);

        //维持并查集的性质
        f[r[x]]=x;
        if(d[l[x]]<d[r[x]])
            swap(l[x],r[x]);
        d[x]=d[r[x]]+1;
        return x;
    }
    int Get_root(int x){
        //并查集
        //查找编号为x的节点所在的左偏树的根的序号,不需要可以删除
        int r=x;
        while(f[r]!=r)
            r=f[r];
        //路径压缩,直接指向他们的根
        int k;
        while(f[x]!=r){
            k=f[x];
            f[x]=r;
            x=k;
        }
        return r;
    }
public:
    void Init(int n) {
        //使用v[]中的值初始化一片左偏树-并查集森林
        tot=n;
        for(int i=1; i<=n; i++) {
            l[i]=r[i]=d[i]=0;
            //建立并查集,连通块的性质由这里维护
            f[i]=i;
        }
    }
    int Top(int x){
        //返回x所在的左偏树的堆顶
        x=Get_root(x);
        return v[x];
    }
    int Pop(int x) {
        //将x所在的左偏树的堆顶弹出
        x=Get_root(x);
        //题目要求把v[x]删除,这里置为特殊值表示删除
        v[x]=-1;
        //将两个子树合并,相当于删除了堆顶
        //把一棵树的堆顶删除
        int rt=_Merge(l[x],r[x]);
        //在这里把树根的并查集性质导向新的树根rt,并把大家都指向新树根
        siz[x]=siz[l[x]]=siz[r[x]]=siz[x]-1;
        f[x]=f[l[x]]=f[r[x]]=f[rt]=rt;
        return rt;
    }
    bool Merge(int x,int y){
        x=Get_root(x);
        y=Get_root(y);
        if(x==y){
            //原本就是同一棵左偏树里面的
            return false;
        }
        else{
            //在这里更新并查集的信息
            siz[x]=siz[y]=(siz[x]+siz[y]);
            _Merge(x,y);
            return true;
        }
    }
}lt;


int solve() {
    scanf("%d%d",&n,&m);
    for(int i=1; i<=n; i++) {
        scanf("%d",&v[i]);
    }

    lt.Init(n);

    for(int i=1;i<=m;i++){
        int z,x,y;
        scanf("%d%d",&z,&x);
        if(z==1){
            scanf("%d",&y);
            if(v[x]==-1||v[y]==-1)
                continue;
            lt.Merge(x,y);
        }
        else{
            if(v[x]==-1){
                printf("-1
");
            }
            else{
                printf("%d
",lt.Top(x));
                lt.Pop(x);
            }
        }
    }

    return 0;
}

以上是关于模板 - 左偏树 + 并查集的主要内容,如果未能解决你的问题,请参考以下文章

模板 - 左偏树 + 并查集

模板左偏树(可并堆)

模板左偏树

猴子大王Monkey King 左偏树+并查集维护

[bzoj1455]罗马游戏_左偏树_并查集

[BZOJ 1455]罗马游戏(左偏树+并查集)