洛谷p3377 左偏树

前置知识:二叉堆

首先,我们定义 外节点 为左儿子或右儿子为空的节点,定义外节点的 dist 为0,空节点的 dist 为-1 ,其他节点的dist为其到子树中最近的外节点的距离加一。

那么,左偏树是什么?左偏树是一棵二叉树,它不仅具有堆的性质,并且是「左偏」的:每个节点左儿子的 dist 都大于等于右儿子的 dist ,另外,每个节点的dist为其右儿子dist+1。

左偏树是一种可并堆,合并的过程参考下面的代码:

#define ls s[x].son[0]
#define rs s[x].son[1]
struct node{
    int dis,val,son[2],rt;
}s[150010];
int merge(int x,int y){//这里是小根堆的合并 
    if(!x||!y)return x+y;//一个堆为空则返回另一个堆 
    if(s[x].val>s[y].val||(s[x].val==s[y].val&&x>y))swap(x,y);//因为是小根堆,所以要值小的在前面 
    rs=merge(rs,y);//把x,y中小的那个作为根 让根的右儿子与另一个递归合并 因为要满足二叉堆的性质 
    if(s[ls].dis<s[rs].dis)swap(ls,rs);//因为要满足左儿子的dist大于右儿子的dist的性质 
    s[ls].rt=s[rs].rt=s[x].rt=x;//把左右儿子和根本身的父亲都设为根本身 
    s[x].dis=s[rs].dis+1;//根据dist的定义 根的dist为右儿子的dist+1; 
    return x;//返回新根 
}

 

另外 找一个堆的堆顶可以用并查集的路径压缩优化,即:

int get(int x){//并查集的路径压缩 
    if(s[x].rt==x)return x;
    s[x].rt=get(s[x].rt);
    return s[x].rt;
}

假如要删掉一个点:

void pop(int x){ 
    s[x].val=-1;//删点就是把自己的权值设为-1
    s[ls].rt=ls;
    s[rs].rt=rs;//左右儿子的父亲设为左右儿子本身
    s[x].rt=merge(ls,rs);//记得将原来的删的点的左右儿子合并 删的点的父亲指向新根(因为你只是删了一个点 不会使那个堆一分为二) 
}

所以这道题的代码就出来了:

#include<bits/stdc++.h>
#define ls s[x].son[0]
#define rs s[x].son[1]
using namespace std;
int n,t,a,b,c;
struct node{
    int dis,val,son[2],rt;
}s[150010];
int merge(int x,int y){
    if(!x||!y)return x+y;
    if(s[x].val>s[y].val||(s[x].val==s[y].val&&x>y))swap(x,y);
    rs=merge(rs,y);
    if(s[ls].dis<s[rs].dis)swap(ls,rs);
    s[ls].rt=s[rs].rt=s[x].rt=x;
    s[x].dis=s[rs].dis+1;
    return x;
}
int get(int x){
    if(s[x].rt==x)return x;
    s[x].rt=get(s[x].rt);
    return s[x].rt;
}
void pop(int x){
    s[x].val=-1;
    s[ls].rt=ls;
    s[rs].rt=rs;
    s[x].rt=merge(ls,rs);
}
int main(){
    cin>>n>>t;
    s[0].dis=-1;
    for(int i=1;i<=n;i++){
        s[i].rt=i;
        scanf("%d",&s[i].val);
    }
    for(int i=1;i<=t;i++){
        scanf("%d%d",&a,&b);
        if(a==1){
            scanf("%d",&c);
            if(s[b].val==-1||s[c].val==-1)continue;
            int B=get(b),C=get(c);
            if(B!=C){
                s[B].rt=s[C].rt=merge(B,C);
            }
        }else{
            if(s[b].val==-1)printf("-1\n");
            else printf("%d\n",s[get(b)].val),pop(get(b));
        }
    }
    return 0;
}

推荐几个题?

洛谷 1456 monkey king

洛谷 2713 罗马游戏

另外 听说可并堆有stl, 在#include<ext/pb_ds/priority_queue.hpp>里....

 

posted @ 2019-10-31 16:00  passione  阅读(157)  评论(0编辑  收藏  举报