平衡树 Treap

//平衡树 Treap
//维护一个堆使得随机权值小(大)的数始终在上方
//使用随机权值目的:防止出题人卡
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
struct uio{
    int l,r,siz,num,rd,tim;//左儿子,右儿子,子树大小+自己大小,点值,随机权值,出现次数
}treap[100001];//此代码为小根堆 即随机权值小的在上方
int n,size,ans,root;//size树的大小
void update(int k)//更新
{
    treap[k].siz=treap[treap[k].l].siz+treap[treap[k].r].siz+treap[k].tim;
}
void right(int &k)//右旋
{
    int x=treap[k].l;
    treap[k].l=treap[x].r;
    treap[x].r=k;
    treap[x].siz=treap[k].siz;
    update(k);
    k=x;
}
void left(int &k)//左旋
{
    int x=treap[k].r;
    treap[k].r=treap[x].l;
    treap[x].l=k;
    treap[x].siz=treap[k].siz;
    update(k);
    k=x;
}
void insert(int &k,int x)
{
    if(k==0)//到达叶节点就开始插入
    {
        size++;
        k=size;
        treap[k].siz=1;
        treap[k].tim=1;
        treap[k].num=x;
        treap[k].rd=rand();
        return;
    }
    treap[k].siz++;//插入节点的每个父节点子树大小都加一
    if(treap[k].num==x)//若已有此节点
        treap[k].tim++;//出现次数加一
    else
    {
        if(x>treap[k].num)//在右子树中
        {
            insert(treap[k].r,x);
            if(treap[treap[k].r].rd<treap[k].rd)//子节点随机数比父节点小
                left(k);//左旋
        }
        else//在右子树中
        {
            insert(treap[k].l,x);
            if(treap[treap[k].l].rd<treap[k].rd)//子节点随机数比父节点小
                right(k);//右旋
        }
    }
}
void del(int &k,int x)
{
    if(k==0)//树中已无节点
        return;
    if(treap[k].num==x)//找到了
    {
        if(treap[k].tim>1)//出现次数大于一,直接删除即可
        {
            treap[k].tim--;
            treap[k].siz--;
            return;
        }
        if(treap[k].l*treap[k].r==0)//左右子树有一棵为空
            k=treap[k].l+treap[k].r;//直接把非空子树接在原树上
        else
        {
            if(treap[treap[k].l].rd<treap[treap[k].r].rd)//每次下沉时与随机权值小的交换 以确保小根堆性质不变
                right(k),del(k,x);
            else left(k),del(k,x);
        }
    }
    else//没找到
    {
        if(x>treap[k].num)//在右子树中
        {
            treap[k].siz--;
            del(treap[k].r,x);
        }
        else//在左子树中
        {
            treap[k].siz--;
            del(treap[k].l,x);
        }
    }
}
int get_no(int k,int x)
{
    if(k==0)//树中没有节点
        return 0;
    if(treap[k].num==x)//找到x
        return treap[treap[k].l].siz+1;//结果为自己加左子树的大小
    else if(x>treap[k].num)//在右子树
        return treap[treap[k].l].siz+treap[k].tim+get_no(treap[k].r,x);//结果为递归回的结果加自己的大小加左子树的大小
    else return get_no(treap[k].l,x);//在左子树  结果为递归回的结果
}
int get_num(int k,int x)
{
    if(k==0)//树中没有节点
        return 0;
    if(x<=treap[treap[k].l].siz)//在左子树中
        return get_num(treap[k].l,x);
    else if(x>(treap[treap[k].l].siz+treap[k].tim))//在右子树中
        return get_num(treap[k].r,x-treap[treap[k].l].siz-treap[k].tim);
    else return treap[k].num;//在这个点上
}
void pre(int k,int x)
{
    if(k==0)
        return;
    if(treap[k].num<x)//在右子树中
    {
        ans=k;
        pre(treap[k].r,x);
    }
    else pre(treap[k].l,x);//在左子树中
}
void nxt(int k,int x)
{
    if(k==0)
        return;
    if(treap[k].num>x)//在左子树中
    {
        ans=k;
        nxt(treap[k].l,x);
    }
    else nxt(treap[k].r,x);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        if(u==1)
            insert(root,v);
        if(u==2)
            del(root,v);
        if(u==3)
            printf("%d\n",get_no(root,v));
        if(u==4)
            printf("%d\n",get_num(root,v));
        if(u==5)
        {
            pre(root,v);
            printf("%d\n",treap[ans].num);
        }
        if(u==6)
        {
            nxt(root,v);
            printf("%d\n",treap[ans].num);
        }
    }
     return 0;
}

原文地址:https://www.cnblogs.com/water-radish/p/9280875.html

时间: 2024-08-27 13:27:40

平衡树 Treap的相关文章

hiho 1325 : 平衡树&#183;Treap

#1325 : 平衡树·Treap 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Ho:小Hi,我发现我们以前讲过的两个数据结构特别相似. 小Hi:你说的是哪两个啊? 小Ho:就是二叉排序树和堆啊,你看这两种数据结构都是构造了一个二叉树,一个节点有一个父亲和两个儿子. 如果用1..n的数组来存储的话,对于二叉树上的一个编号为k的节点,其父亲节点刚好是k/2.并且它的两个儿子节点分别为k*2和k*2+1,计算起来非常方便呢. 小Hi:没错,但是小Hi你知道有一种办

算法模板——平衡树Treap 2

实现功能:同平衡树Treap 1(BZOJ3224 / tyvj1728) 这次的模板有了不少的改进,显然更加美观了,几乎每个部分都有了不少简化,尤其是删除部分,这个参照了hzwer神犇的写法,在此鸣谢,然后,贴模板走人 1 var 2 i,j,k,l,m,n,head,tot:longint; 3 a,b,lef,rig,fix:array[0..100010] of longint; 4 function min(x,y:longint):longint; 5 begin 6 if x<y

luoguP3369[模板]普通平衡树(Treap/SBT) 题解

链接一下题目:luoguP3369[模板]普通平衡树(Treap/SBT) #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #incl

算法模板——平衡树Treap

实现功能如下——1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大的数)6. 求x的后继(后继定义为大于x,且最小的数) 本程序的实现原理为Treap平衡树 详见BZOJ3224 1 var 2 i,j,k,l,m,n,head,ts:longint;f1:text; 3 a,b,fix,lef,rig:array[0..500000] of longint; 4

BZOJ3224: Tyvj 1728 普通平衡树[treap]

3224: Tyvj 1728 普通平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 9046  Solved: 3840[Submit][Status][Discuss] Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小

各种平衡树Treap/SBT/Avl/Splay tree

看了这么久的平衡树,是时候做个总结了. 以poj 3481为例,敲了四份代码,分别是Treap ,Size Balance Tree,Avl Tree,splay tree. 唯独少了红黑树T_T... 总的来说每种平衡树各有各的优点吧: Treap写起来简单上手也快如果熟练的话不到十分种可以敲完. SBT速度快名不虚传... Avl树高度平衡吧,不过实际的效果不尽如人意,可能是我实现的姿势不对吧/(ㄒoㄒ)/~~ splay tree各方面比较均衡,特别的伸展树在维护序列方面相对其它几种树优势

【bzoj3224】普通平衡树——treap

我的第一道treap题目,treap的模版题. 代码是对着hzw的敲的,一边敲一边理解... 主要是熟悉一下treap的各种基本操作,详细细节看代码. #include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> using namespace std; struct point { int l,r,v,rnd,size,w; }tree[100005]; int n,size

BZOJ 3224: Tyvj 1728 普通平衡树 treap

3224: Tyvj 1728 普通平衡树 Description 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数,因输出最小的排名)4. 查询排名为x的数5. 求x的前驱(前驱定义为小于x,且最大的数)6. 求x的后继(后继定义为大于x,且最小的数) Input 第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

BZOJ_3224_普通平衡树(Treap)

描述 Treap模板题.支持如下几种操作: 1.插入; 2.删除; 3.rank(x); 4.kth(k); 5.pre(x); 6.suc(x); 分析 写多了就熟了...昨天看了书,今天调试的时候敲了好多遍,感觉满脑子是tree+heap. p.s. 1.rank,kth,pre,suc函数都可以写成非递归的形式. 2.remove函数有两种写法: (1).LRJ白书上的方法: 如果待删除节点只有一个子树,则用该子树代替待删除节点,删除待删除节点即可.如果有两个子树,则将优先值小的(小根堆)