洛谷 - P3377 - 【模板】左偏树(可并堆) - 左偏树 - 并查集

https://www.luogu.org/problemnew/show/P3377
左偏树+并查集

左偏树维护两个可合并的堆,并查集维护两个堆元素合并后可以找到正确的树根。

关键点在于删除一个堆的堆根的时候,需要把原来堆根的父指针指向新的堆根。这样并查集的性质就不会被破坏了。

#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();
}

const int MAXN=100005;
int tot,v[MAXN],l[MAXN],r[MAXN],d[MAXN],f[MAXN];
//这其实是一片左偏树森林,用处不大
struct Leftist_Tree{
    int Merge(int x,int y){
        //将两棵左偏树,返回他们的新根
        //当其中一棵是空树,直接返回另一棵
        if(!x||!y)
            return x+y;
        //此处符号决定是最小堆还是最大堆
        if(v[x]>v[y]||(v[x]==v[y]&&x>y))
            swap(x,y);
        //维持x不比y大,相等则把小的那个作为根
        //把大数y插入小数x的右子树,所以是最小堆
        r[x]=Merge(r[x],y);
        if(r[x]==x)
            r[x]=0;
        //保证左子树比右子树高
        //新的右子树的根的父亲是x,不需要可以删除
        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 Build(int x){
        //新建一棵只有x的左偏树,返回他的编号
        tot++;
        v[tot]=x;
        //新树的节点的父亲为他本身,不需要可以删除
        f[tot]=tot;
        l[tot]=r[tot]=d[tot]=0;
        return tot;
    }
    int Push(int x,int y){
        //向编号为x的左偏树插入值为y的节点,其实就是新建只有y的左偏树然后合并
        return Merge(x,Build(y));
    }
    int Top(int x){
        //直接返回编号为x的左偏树的堆顶
        return v[Get_root(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;
    }
    int Pop(int x){
        //将两个子树合并,相当于删除了堆顶
        //return Merge(l[x],r[x]);
        //当需要保留查询编号为x的节点在哪个子树时,不能够把堆顶丢掉
        //把编号为x的元素删除
        v[x]=-1;
        //根x被删除,从x反过来指向他们的孩子就可以了
        int rt=Merge(l[x],r[x]);
        f[x]=f[l[x]]=f[r[x]]=rt;
        return rt;
    }
};

int solve() {
    int n,m;
    scanf("%d%d",&n,&m);
    //printf("n,m=%d,%d\n",n,m);
    Leftist_Tree lt;
    f[0]=0;
    v[0]=-1;
    tot=0;
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        //printf("x=%d\n",x);
        lt.Push(i,x);
        /*for(int j=1;j<=tot;j++){
            printf("%d ",v[j]);
        }
        printf("\n");*/
    }
    for(int i=1;i<=m;i++){
        int z,x,y;
        scanf("%d",&z);
        //printf("z=%d\n",z);
        if(z==1){
            scanf("%d%d",&x,&y);
            //printf("x=%d y=%d\n",x,y);
            if(v[x]==-1||v[y]==-1)
                continue;
            lt.Merge(lt.Get_root(x),lt.Get_root(y));
        }
        else{
            scanf("%d",&x);
            if(v[x]==-1){
                printf("-1\n");
                continue;
            }
            //printf("x=%d\n",x);
            int fx=lt.Get_root(x);
            printf("%d\n",lt.Top(fx));
            lt.Pop(fx);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Yinku/p/10957790.html