fhq Treap

普通Treap,既Tree加Heap。它让平衡树上的每一个结点存放两个信息:值和一个随机的索引。其中值满足二叉搜索树的性质,索引满足堆的性质,结合二叉搜索树和二叉堆的性质来使树平衡。这也是Treap的性质。

Treap用二叉堆来维护随机索引,其实就是相当于把插入次序随机化。插入一数值后你必然要让索引去满足二叉堆的特性,但又因为索引是随机的,那就会导致插入的数后不知道搞到哪里去了,相当于插入次序随机了。

(随机大法好,吊打一切毒瘤数据)

fhq Treap的奇怪操作并且是核心操作只有两个:分裂和合并

就是把树拆开再拼上拆开再拼上

我们需要保存五个信息:

左右子树编号、值、索引、子树大小

分裂:

分裂有两种:按值分裂和按大小分裂

按值分裂:把树拆成两棵树,拆出来的一棵树的值全部小于等于给定的值,另外一部分的值全部大于给定值

按大小分裂:把树拆成两棵树,拆出来的一棵树的值全部小于给定大小,另一部分的值全部大于等于给定的大小

一般来说,fhq Treap当一颗正常的平衡树使用的时候,使用按值分裂

而在维护区间信息的时候,使用按大小分裂。(文艺平衡树)

例如对这棵树按值17分裂

合并:

合并就一种操作,就是把两棵树x,y合并成一棵树,其中x树上的所有值都小于等于y树上的所有值,并且新合并出来的树依旧满足Treap的性质

可是这两种操作有啥用,我怎么才能用这两种操作来完成平衡树的基操呢?

首先来说插入

设插入的值为val,那么我们的步骤是:

按值val把树分裂成x和y

合并x,新节点,y

首先要插入val这个值,那么我们先按val分裂,于是得到了两棵树x和y, 按照按值分裂的定义,我们分裂出来的的x树上的所有值一定小于等于val,y树上的所有值一定都大于val

那么我们直接合并x,用值val新建的新节点,y就ok了

删除:

不如插入简单,要四步:

例如要删除val这个值,那么:

首先按值val把树分裂成x和z,再按值val-1把x树分裂成x和y

那么此时y树上的所有值都是等于val的,我们去掉它的根结点,让y等于合并y的左子树和右子树,最后,合并x,y,z

查询值的排名

设要查询的值为val,那么按值val-1分裂成x和y,则x的大小+1就是val的排名,最后再把x和y合并起来

前驱/后继

设操作数val

前驱:按值val-1分裂成x和y,则x里面最优的数就是val的前驱

后记:按值val分裂成x和y,则y里面最左的数就是val的后继

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
struct node{
    int l, r;
    int val, key;
    int size;
}fhq[maxn];

int cnt, root;
std::mt19937 rnd(233);
inline int newnode(int val){
    fhq[++cnt].val = val;
    fhq[cnt].key = rnd();
    fhq[cnt].size = 1;
    return cnt;
}
inline void update(int now){
    fhq[now].size = fhq[fhq[now].l].size + fhq[fhq[now].r].size + 1;
}
void split(int now, int val, int &x, int &y){
    if(!now) x = y = 0;
    else{
        if(fhq[now].val <= val){
            x = now;
            split(fhq[now].r, val, fhq[now].r, y);
        }
        else{
            y = now;
            split(fhq[now].l, val, x, fhq[now].l);
        }
        update(now);
    }
}
int merge(int x, int y){
    if(!x || !y) return x + y;
    if(fhq[x].key > fhq[y].key){
        fhq[x].r = merge(fhq[x].r, y);
        update(x);
        return x;
    }
    else{
        fhq[y].l = merge(x, fhq[y].l);
        update(y);
        return y;
    }
}
int x, y, z;
inline void ins(int val){
    split(root, val, x, y);
    root = merge(merge(x,newnode(val)), y);
}
inline void del(int val){
    split(root,val,x, z);
    split(x, val-1, x, y);
    y = merge(fhq[y].l, fhq[y].r);
    root = merge(merge(x, y), z);
}
inline void getrank(int val){
    split(root, val - 1, x, y);
    cout << fhq[x].size + 1 << "\n";
    root = merge(x, y);
}
inline void getnum(int rank){
    int now = root;
    while(now){
        if(fhq[fhq[now].l].size + 1 == rank){
            break;
        }
        else if(fhq[fhq[now].l].size >= rank) now = fhq[now].l;
        else{
            rank -= fhq[fhq[now].l].size + 1;
            now = fhq[now].r;
        }
    }
    cout << fhq[now].val << "\n";
}
inline void pre(int val){
    split(root, val - 1, x, y);
    int now = x;
    while(fhq[now].r) now = fhq[now].r;
    cout << fhq[now].val << "\n";
    root = merge(x, y);
}
inline void nxt(int val){
    split(root, val, x, y);
    int now = y;
    while(fhq[now].l) now = fhq[now].l;
    cout << fhq[now].val << "\n";
    root = merge(x, y);
}
int main(){
    int t;
    cin >> t;
    while(t--){
        int opt, x;
        cin >> opt >> x;
        switch(opt){
            case 1:
                ins(x);
                break;
            case 2:
                del(x);
                break;
            case 3:
                getrank(x);
                break;
            case 4:
                getnum(x);
                break;
            case 5:
                pre(x);
                break;
            case 6:
                nxt(x);
                break;
        }
    }
    return 0;
}

感谢氢氧化银大佬

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值