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

题目描述



代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct trr{
    int dis,val,ls,rs,rt;
}tr[maxn];
/*
dis:记录这个节点到它子树里面最近的叶子节点的距离,叶子节点距离为0
val:节点的权值
ls:左儿子
rs:右儿子
rt:根节点编号
*/
int bing(int xx,int yy){
    if(!xx || !yy) return xx+yy;
    //叶子节点直接返回
    if(tr[xx].val>tr[yy].val ||(tr[xx].val==tr[yy].val && xx>yy)){
        swap(xx,yy);
    }//维护小根堆的性质
    tr[xx].rs=bing(tr[xx].rs,yy);//将xx的右儿子和yy合并
    if(tr[tr[xx].ls].dis<tr[tr[xx].rs].dis) swap(tr[xx].ls,tr[xx].rs);//维护左偏性质
    tr[tr[xx].ls].rt=tr[tr[xx].rs].rt=tr[xx].rt=xx;//维护根节点
    tr[xx].dis=tr[tr[xx].rs].dis+1;//一个节点的距离始终等于右儿子+1
    return xx;
}
int get(int xx){
    if(tr[xx].rt==xx) return xx;
    return tr[xx].rt=get(tr[xx].rt);
    
}//路径压缩查找根节点
void sc(int xx){
    tr[xx].val=-1;
    tr[tr[xx].ls].rt=tr[xx].ls;
    tr[tr[xx].rs].rt=tr[xx].rs;
    tr[xx].rt=bing(tr[xx].ls,tr[xx].rs);
}//删除某个节点
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        tr[i].rt=i;
        scanf("%d",&tr[i].val);
    }
    for(int i=1;i<=m;i++){
        int aa;
        scanf("%d",&aa);
        if(aa==1){
            int bb,cc;
            scanf("%d%d",&bb,&cc);
            if(tr[bb].val==-1 || tr[cc].val==-1) continue;
            int xx=get(bb),yy=get(cc);
            if(xx!=yy) tr[xx].rt=tr[yy].rt=bing(xx,yy);
        } else {
            int bb;
            scanf("%d",&bb);
            if(tr[bb].val==-1) printf("-1\n");
            else {
                printf("%d\n",tr[get(bb)].val);
                sc(get(bb));
            }
        }
    }
    return 0;
}