非旋treap

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 100005
struct hh
{
    int siz;//siz以这个点为根的子树大小 
    int val;//val值满足二叉搜索树的性质(lch<now<rch) 
    int key;//key值满足堆的性质(大堆或小堆) 
    int lch;//lch左子节点
    int rch;//rch右子节点 
}t[maxn];
int tot,seed=233,root=1;
int Rand(); //给k赋予随机优先级 
inline ll read()//快速读入程序 
{
    char kr=0;
    char ls;
    for(;ls>'9'||ls<'0';kr=ls,ls=getchar());
    ll xs=0;
    for(;ls>='0'&&ls<='9';ls=getchar())
    {
        xs=xs*10+ls-48;
    }
    if(kr=='-')
    xs=0-xs;
    return xs;
}
int Rand()
{//随机key值 
    return seed=int(seed*482711ll%2147483647);/**/
}
int NEW(int val)//新建节点 
{
    t[++tot].siz=1;
    t[tot].val=val;
    t[tot].key=Rand();
    t[tot].lch=t[tot].rch=0;
    return tot;
}
void update(int now)//维护子树大小 
{
    t[now].siz=t[t[now].lch].siz+t[t[now].rch].siz+1;
}
void split(int now,int &a,int &b,int val)//拆分操作 
{//now为原treap,a为左子树,b为右子树,val为判定值(注意传地址符) 
    if(now==0)
    {
        a=b=0;//若now=0分割完毕 
        return ;
    }
    if(t[now].val<=val)//因为now左子树中的所有值都小于now的值,所以若now属于左子树,那么他们都属于左树递归now的右子树;
    a=now,split(t[now].rch,t[a].rch,b,val);//a=now已经使a的右子树=now的右子树,不再递归a的右子树; 
    else//同上now的右子树也都属于左树,递归左子树; 
    b=now,split(t[now].lch,a,t[b].lch,val);
    update(now);//因为后面会用到左(右)树的siz所以更新维护 
}
void merge(int &now,int a,int b)//合并操作,now新树 
{
    if(a==0||b==0)
    {
        now=a+b;//若某个数已空,则将另一个子树整体插入 
        return ;
    }
    //按照key值合并(堆性质)
    if(t[a].key<t[b].key)
    //若a树key值<b树,那么b树属于a树的后代,因为b树恒大于a树,那么b树一定属于a树的右后代,a的左子树不变,直接赋值给now,递归合并a的右子树和b
    now=a,merge(t[now].rch,t[a].rch,b); 
    else//同理,a树一定是b树的左儿子,递归合并b的右子树和a 
    now=b,merge(t[now].lch,a,t[b].lch);
    update(now);//维护一下合并后的树的大小 (siz)
}
void insert(int val)//插入一个数 
{
    int x=0,y=0,z;
    z=NEW(val);//新建节点z,作为z树
    split(root,x,y,val);//将树分为两部分,x树为<=待插入的值,y树大于 
    merge(x,x,z);//合并x树和新节点z(树),赋值给x树 
    merge(root,x,y);//合并新x树和y树,赋值给根 
}
void delet(int val)//删除一个数 
{
    int x=0,y=0,z=0;
    split(root,x,y,val);//分为x树为<=待删除,y树大于 
    split(x,x,z,val-1);//x树分为新x树<待删除,z树等于待删除
    merge(z,t[z].lch,t[z].rch);//合并z树的左右儿子,赋给z树,即丢弃z树的根节点(实现删除) 
    merge(x,x,z);
    merge(root,x,y);//合并,不再重复 
}
void get_rank(int val)//求k数的排名 
{
    int x=0,y=0;
    split(root,x,y,val-1);//分为小于待查找的x树和大于等于的y树 
    printf("%d\n",t[x].siz+1);//即为待查找值的编号
    merge(root,x,y);//合并 
}
void find(int now,int rank)//兼容 
{//兼容性函数->用来 查询排名第k位的数,查询k的前驱、后继
    while(t[t[now].lch].siz+1!=rank)
    {
        if(t[t[now].lch].siz>=rank)
            now=t[now].lch;//若左子树大小大于rank,找左子树
        else 
        {
            rank-=(t[t[now].lch].siz+1),now=t[now].rch;
            //找右子树(rank-左子树大小-树根(大小为1))号的元素
        } 
    }
    printf("%d\n",t[now].val);
}
void get_val(int rank)//求排名第k为的数 
{
    find(root,rank);//find查找即可
}
void get_pre(int val)//求k的前驱
{
    int x=0,y=0;
    split(root,x,y,val-1);//x树为<=val-1值即小于val值 
    find(x,t[x].siz);//在小于val值中找到最大的(编号为siz)就是前驱
    merge(root,x,y);//合并 
}
void get_nxt(int val)
{
    int x=0,y=0;
    split(root,x,y,val);//x树小于等于val值,那么y树大于val值
    find(y,1);//在y树中找最小的,即为后继
    merge(root,x,y);//合并 
}
int main()
{
    int i,j,k,m;
    NEW(2147483627);//初始化虚节点 
    t[1].siz=0;//siz为0,不算虚节点的大小
    m=read(); 
    while(m--)
    {
        j=read();k=read();
        if(j==1) insert(k);//插入一个数k 
        if(j==2) delet(k);//删除一个数k 
        if(j==3) get_rank(k);//查询k数的排名
        if(j==4) get_val(k);//查询排名第k位的数 
        if(j==5) get_pre(k);//求k的前驱(定义为小于x,且为最大的数)
        if(j==6) get_nxt(k);//求k的后继(定义为大于x,且为最小的数) 
    }
    return 0;
}

 

posted @ 2018-08-28 09:45  落笔映惆怅丶  阅读(262)  评论(1编辑  收藏  举报