P3377 【模板】左偏樹(可並堆) 左偏樹淺談

由於也是昨天剛接觸左偏樹,從頭理解,若有不慎之處,跪請指教ios

左偏樹:函數

什 麼是(fzy說)左偏樹啊?spa

前置知識:code

  左偏樹中dist:表示到右葉點(就是一直往右下找,最後一個)的距離,特別的,無右節點的爲0。blog

  堆:左偏樹是個堆。排序

  關於左偏性質:能夠幫助堆合併(研究深了我也不懂的,看代碼理解遞歸

  對於任意的節點,dist[leftson]>=dist[rightson],體現了左偏性質。get

  同理可得:對於任意右兒子的父親節點的dist天然等於右兒子的dist+1嘍string

關於各類操做:io

merge:

  是插入操做的函數,具體步驟以下:

  1.對於兩個堆x,y,判斷x,y是否爲0,若是有一個爲0,至關於沒合併,直接返回另外一個有元素的堆。

  2.找到value值更大的那個堆頭放到頂上,若是value值同樣的話就按堆頂編號來排序。爲了方便代碼實現,咱們能夠規定x爲符合條件(小,大跟堆)的那個堆頭,而後若是y符合條件就交換x,y值。

  3.既然堆頭找着了,就能夠進一步的合併堆頭右兒子和y堆了(爲了儘可能保證左偏的性質)。如此遞歸下去,隨着新堆頭被一次次肯定,最終這個堆會被一點一點融合到另外一個堆中。

  4.可是,鑑於合成完後,不必定可以保證左子樹的dist值必定會比右字數的大,咱們只要判斷一下是否符合左偏性質,若是不符合,就交換一下當前節點左右子樹就好了。由於是遞歸執行,從更深節點一層一層上來,那麼必然的整個堆會符合左偏性質。而後更新一下dist爲右子樹dist+1.一次merge完成。

代碼:

inline int merge(int x, int y)
{
    if(!x||!y)return x+y;
    if(tree[x].value>tree[y].value||(tree[x].value==tree[y].value&&x>y))swap(x,y);
    rs=merge(rs,y);
    if(tree[ls].dist<tree[rs].dist)swap(ls, rs);tree[ls].rt=tree[rs].rt=tree[x].rt=x,tree[x].dist=tree[rs].dist+1;
    //更新dist 
     return x ;
}

 2.pop彈出函數:

彈出函數,即彈出堆頂。方法很簡單:沒有了堆頂,整個左偏樹就被分爲了兩個小的左偏樹。咱們只要忽略掉堆頂合併(merge)兩個小的左偏樹便可。

注意事項:不要忘了堆頂元素相關信息還原爲初始。

代碼:

inline void pop(int x)//彈出x爲堆頂的堆 
{
    tree[x].value=-1,tree[ls].rt=ls,tree[rs].rt=rs;
    tree[x].rt=merge(ls,rs);
} 

 3.get函數:

沒啥可說的,就是並查集找父親而且路徑壓縮。

代碼:

int get(int x)
{
    return x==tree[x].rt?x:tree[x].rt=get(tree[x].rt);
} 

三個函數代碼已經完結。

main函數內根據題意進行模擬便可。

總代碼:

#include<queue>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#define N 100003
#define ls tree[x].son[0]
#define rs tree[x].son[1]
using namespace std;
int read()
{
    int ans=0;
    char ch=getchar(),last=' ';
    while(ch<'0'||ch>'9')last=ch,ch=getchar();
    while(ch>='0'&&ch<='9')ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
    return last=='-'?-ans:ans; 
}
inline void swap(int &x,int &y)
{
    x^=y^=x^=y;
}
int n,num,hea[N],t,judge,b,c;
struct tre{
    int son[2],rt,dist,value;
}tree[N];
inline int merge(int x, int y)
{
    if(!x||!y)return x+y;
    if(tree[x].value>tree[y].value||(tree[x].value==tree[y].value&&x>y))swap(x,y);
    rs=merge(rs,y);
    if(tree[ls].dist<tree[rs].dist)swap(ls, rs);tree[ls].rt=tree[rs].rt=tree[x].rt=x,tree[x].dist=tree[rs].dist+1;
    //更新dist 
     return x ;
}
int get(int x)
{
    return x==tree[x].rt?x:tree[x].rt=get(tree[x].rt);
} 
inline void pop(int x)//彈出x爲堆頂的堆 
{
    tree[x].value=-1,tree[ls].rt=ls,tree[rs].rt=rs;
    tree[x].rt=merge(ls,rs);
} 
int main(){
    n=read(),t=read();tree[0].dist=-1;
    for (int i=1;i<=n;i++) 
        tree[i].rt=i,scanf("%d",&tree[i].value);//並差集初始化+輸入
    for (int i=1;i<=t;i++){
        judge=read(),b=read();
        if (judge==1){
            c=read();
            if (tree[b].value==-1||tree[c].value==-1) continue ;
            int f1=get(b),f2=get(c);if(f1!=f2)tree[f1].rt=tree[f2].rt=merge(f1,f2);//合併操做
        }
        else {
            if(tree[b].value==-1)printf("-1\n") ;
            else printf("%d\n",tree[get(b)].value),pop(get(b)) ;//輸出並彈出
        }
    }
    return 0 ;
}

完結。

 彩蛋:有趣的東西:

極度真實的左偏樹。

 

 來自dalao

相關文章
相關標籤/搜索