1. 程式人生 > >學習筆記:fhq treap

學習筆記:fhq treap

-1. Lead in

某一天,蒟蒻Vegetable_Duck突然想學平衡樹,百度一下,發現基本都要旋轉,這對蒟蒻Vegetable_Duck是致命地打擊QAQ.一籌莫展之際,它突然看到了一種不用旋轉的平衡樹——fhq treap

0. 前置知識:treap的定義

樹堆,在資料結構中也稱Treap,是指有一個隨機附加域滿足堆的性質的二叉搜尋樹,其結構相當於以隨機資料插入的二叉搜尋樹。 ——摘自百度百科

形象化一點:

  • treap是關於val的二叉搜尋樹
  • treap是關於rdm的二叉搜尋樹 為了滿足上面兩條性質,就要分情況對
    treap
    進行旋轉 怎麼轉我也不會……因為這是介紹fhq treap的文章

1.fhq treap

基本定義&操作

解釋下陣列含義

int son[N][2]; //0:左兒子 1:右兒子
int sz[N];//子樹大小
int val[N];//權值
int rdm[N];//隨機權值
int cnt;//總treap的節點個數

如何開一個新節點

int new_node(int x) {
    val[++cnt]=x;//節點權值
    rdm[cnt]=rand();//隨機權值
    sz[cnt]=1;//子樹大小
    return
cnt; }

pushup

inline void pushup(int x) {
    sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
}

核心操作

fhq treap的核心操作只有兩種:splitmerge,這兩種操作巧妙玄學地讓每次插入和刪除,treap的性質都不會被破壞,自然也就不用旋轉

merge

把a、b兩棵treap合成一棵大treap的操作 滿足a所有節點的權值 < b所有節點的權值 這樣就只用維護隨機權值的heap性質

程式碼:

void merge(int &now,int x,int y) { //分別為根節點地址,a,b 
    if(!x || !y) {
        now=x+y;
        return;
    } 
    if(rdm[x]<rdm[y]) { //維護rdm堆
        now=x;//保留a的左子樹
        merge(son[now][1],son[now][1],y);//把b塞進a的右子樹,維護bst
    }else { //原理同上
        now=y;
        merge(son[now][0],x,son[now][0]);
    }
    pushup(now);
}

split

把一棵大treap按照某種劃分標準分成ab兩棵treap的操作

常用的劃分標準有權值val和子樹大小sz,兩種程式碼都會給

程式碼:

  • 按val劃分
//劃分後,a中所有元素<=k,b中所有元素>k
void split(int now,int k,int &x,int &y) {//分別為:當前節點,劃分標準,a的樹根,b的樹根
    if(!now) {//沒東西了
        x=y=0;
        return;
    }
    if(val[now]<=k) {//利用bst的性質
        x=now;
        split(son[now][1],k,son[now][1],y);
    }else {
        y=now;
        split(son[now][0],k,x,son[now][0]);
    }
    pushup(now);
}
  • 按sz劃分
//把大treap中前k小的元素放到a中,剩下放到b中
void split(int now,int k,int &x,int &y) {
    if(!now) {//沒東西了
        x=y=0;
        return;
    }
    if(sz[son[now][0]]>k) {
        x=now;
        split(son[now][1],k-sz[son[now][0]]-1,son[now][1],y);
    }else {
        y=now;
        split(son[now][0],k,x,son[now][0]);
    }
    pushup(now);
}

2.模板題

洛谷 3369/BZOJ 3224/Tyvj 1728 普通平衡樹

#include <bits/stdc++.h>
#define N 100005

using namespace std;

int root=0;

struct fhq_treap {
    int son[N][2],sz[N],val[N],rdm[N],cnt;

    inline int new_node(int x) {
        val[++cnt]=x,rdm[cnt]=rand(),sz[cnt]=1;
        return cnt;
    }

    inline void pushup(int x) {
        sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
    }

    void split(int now,int k,int &x,int &y) {
        if(!now) {
            x=y=0;
            return;
        }
        if(val[now]<=k) {
            x=now;
            split(son[now][1],k,son[now][1],y);
        }else {
            y=now;
            split(son[now][0],k,x,son[now][0]);
        }
        pushup(now);
    }

    void merge(int &now,int x,int y) { 
        if(!x || !y) {
            now=x+y;
            return;
        } 
        if(rdm[x]<rdm[y]) {
            now=x;
            merge(son[now][1],son[now][1],y);
        }else {
            now=y;
            merge(son[now][0],x,son[now][0]);
        }
        pushup(now);
    }

    void kth(int now,int k) {//第k大數
        while(1) {
            if(sz[son[now][0]]>=k) {
                now=son[now][0];
            }else {
                if(sz[son[now][0]]+1==k) break;
                k-=(sz[son[now][0]]+1);
                now=son[now][1];
            }
        }
        printf("%d\n",val[now]);
    }
}T;

int main() {
    int n,op,x,a,b,c;
    scanf("%d",&n);
    T.sz[0]=0; 
    T.cnt=0;
    while(n--) {
        a=0,b=0;
        scanf("%d%d",&op,&x);
        if(op==1) { //插入x數
            T.split(root,x,a,b);
            int t=T.new_node(x);
            T.merge(a,a,t);
            T.merge(root,a,b);
        }
        if(op==2) { //刪除x數
            T.split(root,x,a,b);
            T.split(a,x-1,a,c);
            T.merge(c,T.son[c][0],T.son[c][1]);
            T.merge(a,a,c);
            T.merge(root,a,b);
        }       
        if(op==3) { //查詢x數的排名
            T.split(root,x-1,a,b);
            printf("%d\n",T.sz[a]+1);
            T.merge(root,a,b);          
        }
        if(op==4) { //查詢排名為x的數
            T.kth(root,x);      
        }       
        if(op==5) { //求x的前驅
            T.split(root,x-1,a,b);
            T.kth(a,T.sz[a]);
            T.merge(root,a,b);          
        }
        if(op==6) { //求x的後繼
            T.split(root,x,a,b);
            T.kth(b,1);
            T.merge(root,a,b);          
        }       
    }
}