Treap学习笔记
前言:没有人比我更容易RE
我就用必定Runtime Excellent的指针来写*衡树。
definition
Treap是*乎*衡的二叉查找树。二叉查找树满足左儿子的权值小于父亲,右儿子的权值大于父亲。
同时由于这样的二叉查找树很容易退化成链,所以我们给树的每个节点随机赋予一个随机权值,并且要求树上关于这个随机权值满足堆的性质。这样随机出来的结果大部分都会*乎*衡,几乎不会退化成链。
build
定义节点
typedef long long int lld;
typedef long long unsigned llu;
class Treap
{
/// use val-0 for val, ltree less than 0, rtree greater than 0
/// cnt0 can be 0, means there is no 0
/// but root should be saved as root
public:
lld val,rd;
llu sz[2],cnt;
// sz[0] for size without repetition
// sz[1] for size with repetition
Treap *lson,*rson;
Treap(const lld=inf);
~Treap(){}
Treap* maintain(const int=0);
void cut();
Treap operator=(const Treap a)
{
val=a.val; cnt=a.cnt; sz[0]=a.sz[0]; sz[1]=a.sz[1]; rd=a.rd;
return *this;
}
operator long long int()const
{
return val;
}
void print(int=0);
};
英语不太好……偶来解释一下啦
定义结构类型Treap,一个Treap变量就表示一个节点。所以每个节点会有以下几个属性:
Treap* lson 指向左儿子地址的指针,没有左儿子时为空
Treap* rson 指向右儿子地址的指针,没有右儿子时为空
lld val 这个节点储存的权值
lld rd 对于Treap节点特别赋予的随机权值。
可以用std::rand()函数获取,也可以自己写
llu cnt 这个节点的个数,也就是*衡树中数值val的个数
llu sz[0] 这棵子树的大小,重复数字只计算一次
llu sz[1] 这棵子树的大小,重复数字按照实际情况计算
Treap* maintain(const int) 更新这个节点的大小
看前面三行注释……我想说明的是,Treap实现的方法有很多,这里我自创了一种方法。
以权值0为根,所以左子树的权值都小于0,右子树的权值都大于0
初始的时候,令树根为rt, rt->cnt=0; rt->val=0; rt->rd=inf; 然后要求随机权值满足大根堆,所以这个根就一定是根,反过来说权值0的节点一定是根,根的权值一定是0
但是这棵树里面一开始应该什么都没有,所以rt->cnt应该初始化为0
Treap::Treap(const lld r)
{
/// init the node, as a root
val=cnt=sz[0]=sz[1]=0;
lson=rson=0;
rd=r;
}
建树
void build(Treap **rt)
{
/// build up a new tree
srand(time(0));
if(*rt==0) *rt=new Treap;
return ;
(*rt)->cut();
delete *rt;
*rt=new Treap;
}
void Treap::cut()
{
/// cut the tree
if(lson) lson->cut(), delete lson;
if(rson) rson->cut(), delete rson;
}
使用这个函数对rt进行初始化。这里rt的数据类型应该是Treap*,但是因为我们要初始化,所以就要传入&rt,也就是Treap**的数据类型,才能修改rt。
如果这是一个空节点,那么直接新建。
否则,先把原来的树砍了,再赋新权值。
当然,我们也可以每次新建树的时候,重新更新随机指针。当然也可以在主函数统一更新。
maintain
维护当前节点的属性
sz[0]是不计算重复下的大小,所以先初始赋值为0/1,sz[1]是计算重复下的大小,所以先初始赋值为cnt
然后我们,如果有儿子,再加上儿子的大小。
特别的,有可能儿子在删除里面删掉了,这个时候儿子的cnt会是0,更新出来儿子的sz[1]也会是0,这个时候我们需要删除儿子释放空间
Treap* Treap::maintain(const int flag)
{
/// update, if flag, update sons first
this->sz[0]=(bool)cnt;
this->sz[1]=cnt;
if(flag)
{
if(lson) lson->maintain(1);
if(rson) rson->maintain(1);
}
if(lson)
{
if(lson->sz[1]) sz[0]+=lson->sz[0], sz[1]+=lson->sz[1];
else delete lson, lson=0;
/// if lson has no size, it is the empty node which has been deleted
}
if(rson)
{
if(rson->sz[1]) sz[0]+=rson->sz[0], sz[1]+=rson->sz[1];
else delete rson, rson=0;
}
return this;
}
这里flag是标记,如果flag为1,那么我们就需要先更新儿子,再来维护父亲。我称其为深维护。
如果flag是0,就说明儿子的属性没有改变,不需要更新儿子。就是浅维护。
最后返回当前节点的指针
rotate
我们都知道,对于新建的树,肯定不可能都满足大根堆性质。这个时候我们就要旋转这棵树了。
我们看看这幅图:
从左往右就是左旋的过程,左旋,右旋(其实我也分不清qwq)我这里定义为左旋A,A将成为左儿子
我们可以发现,初始的BST是满足\(D<B<E<A<E<C<F\),这里圆形代表的是一个节点,矩形代表的是一个子树。除了A之外,这些都有可能是空
我们先假设全部都非空,首先左旋,就是A将会成为右儿子C的左儿子,所以要首先断开AC这条边,然后把C挪上去(其实程序不做这一步,只不过画图为了好理解)然后又因为A将成为左儿子,所以就要把C和C的左儿子E之间的边断开,然后再连上A
然后我们就发现E无家可归,我们可以看一下能把他安置到哪里。首先\(E<C\),所以只能放到左子树里面,又因为\(A<C\),所以只能放到A的右子树。这个时候我们恰好发现A的右子树为空,所以E就成为了A的右子树
总结下来,左旋A,只需要记录下A的右儿子C,把A的位置赋给C(比如说是A的父亲的左儿子),所以旋转的时候需要引用传入,这里下面将insert 和remove 的时候会讲。
然后再令A的右儿子为C原来的左儿子,A为C现在的左儿子。
(对啊,代码很短的)
右旋就……完全反过来
我们可以发现,旋转之后还满足BST的要求,而且更换了树的结构。
比如A->rd=2,C->rd=4,原来的BST不满足大根堆性质。但是我们发现A的右儿子C的随机权值比A大的时候,我们就会左旋,左旋之后C是A的父亲,就满足大根堆性质了
Treap* zig(Treap **id)
{
/// 引用传入,返回旋转之后的父节点的地址
/// left-rotate
if((*id)->val==0) return *id;
// 根节点不允许旋转,旋转了就……整棵树崩了
Treap *p=*id,*r;
if(p->rson==0) return p;
// 没有右儿子旋什么旋
r=p->rson;
p->rson=r->lson; r->lson=p;
p->maintain();
r->maintain();
// 旋转完之后维护子树大小。
// 旋转只改变了A和C的子树,所以先更新A,然后再更新C(C是A的父亲)
return *id=r;
// 更新根节点的位置
}
Treap* zag(Treap **id)
{
/// right-rotate
if((*id)->val==0) return *id;
Treap *p=*id,*l;
if(p->lson==0) return p;
l=p->lson;
p->lson=l->rson; l->rson=p;
p->maintain();
l->maintain();
return *id=l;
}
引用传入建议百度,当然insert里面也会简单涉及一下
insert
插入k,如果当前节点的权值比k大,那就说明k应该在左子树里面,
如果当前节点的权值比k小,那就说明k应该在右子树里面。
如果当前节点的权值等于k,那就说明当前节点的cnt++
特别的,如果我们访问到了空节点,就说明我们需要原地起飞建立一个权值为k的节点
然后,建立完之后,这个节点的rd是随机赋予的,可能会破坏大根堆的性质,我们就需要通过旋转来维护
代码就开始很长了QAQ
Treap* insert(Treap **now,const lld x)
{
/// insert x to the tree whose root is *now
if(*now==0)
{
*now=new Treap(rand());
(*now)->val=x;
(*now)->cnt=1;
return (*now)->maintain();
}
if(x==(*now)->val)
{
(*now)->cnt++;
return (*now)->maintain();
}
Treap *p=NULL;
if(x<(*now)->val)
{
p=insert(&(*now)->lson,x);
}
if(x>(*now)->val)
{
p=insert(&(*now)->rson,x);
}
(*now)->maintain();
const int lrd=(*now)->lson? (*now)->lson->rd:-inf,
rrd=(*now)->rson? (*now)->rson->rd:-inf;
if(lrd>rrd&&lrd>(*now)->rd)
{
zag(now);
}else
if(rrd>lrd&&rrd>(*now)->rd)
{
zig(now);
}
return p;
}
英语不好的本宝宝又来解释ler……
首先传入的是指针的地址。有些小可爱们可能不理解:指针不就是指向地址吗?为什么有指针的地址?
这是因为,指针变量和其他变量,比如说int,long,一样,都是变量(这不废话么),在c++中,变量是存储在内存里面的,每次我们访问变量,就是通过地址找到内存相对应的空间才能访问变量。
变量用取地址符&就可以得到地址,指针就是指向这个地址一种……数据类型?
为什么我们要地址呢?因为众所周知,函数传入参数并不是直接传进去,而是复制一个副本传进去,所以
void fun(int x) { x=10; }
int a=20;
fun(a);
经过了这一系列操作之后,出来的a还是20。这是因为函数只是复制了一份a的值赋给了x进行函数操作。
但是如果我们使用引用/指针传入,就可以改变
引用传入:
void fun(int&x) { x=10; }
int a=20;
fun(a);
这样a出来就是10。因为你的引用符号&(其实就是取地址符号)告诉了编译器我这个参数x是引用传递,你必须把它本身拿过来而不是把值复制一份。
指针传入:
void fun(int *x) { *x=10; }
int a=20;
fun(&a);
这里函数传入的就是一个整形指针参数。这里也是复制了一份,但是没有关系,无论是调用时候传入的&a还是执行时候复制出来的x,他们的值都指向内存里存着a的那一块空间的地址,然后就可以通过地址修改变量。
他们修改的都是一个东西……
所以,指针,是指向地址的变量类型。那么存储这个变量类型,自然也要空间啦。要空间就要内存啦。要内存就有地址啦。
所以我们是可以通过Treap**来传入参数,使得函数内可以改变指针的值。
举个栗子,现在我要通过函数把*rt的左儿子设成空
rt->lson=new Treap;
void fun(Treap* x) { x=NULL; }
fun(rt->lson);
这样只是把rt->lson存储的指向一个Treap节点的地址复制了一份给x,rt->lson本身并没有什么改变
但是如果
rt->lson=new Treap;
void fun(Treap **x) { *x=NULL; }
fun(&rt->lson);
(是的这里引用传入会编译错误我也不知道为什么)
这样就是把rt->lson的地址传了进去,这样函数内*x其实就是rt->lson,这样出阿狸之后rt->lson就是NULL了
为什么这里要用指针传入呢?
我们看一下如果现在rt->lson=NULL,rt->val=0,x=-1
那么x应该插入到rt的左儿子,所以递归下去,但是发现当前左儿子为空,所以insert(lson,x)里面就新建了一个节点,但是无论如何,我怎么知道你新建的是什么节点,我怎么更新rt->lson
当然这个时候你可以说,让函数返回新建节点的地址。这样对于直接父亲是可以的。但是如果我递归了好多层最后才发现是空节点,返回回来……不好做啊
反正我想不到
好吧就和你们用数组写的insert(int&id,int x)要用引用传入的原因是一样的
然后……下面就分别是……等于,大于,小于的操作
然后我们插入完了,但是可能会改变堆的性质喔
我们知道插入完从下面递归回来,我们就要先令其满足堆的性质,所以我们可以知道左右儿子一定都满足堆的性质,只是我比较另类而已,特别的,对于叶子结点,我们认为它满足堆的性质(就一个节点你维护什么维护)
然后我们就可以对比一下左右儿子和我的随机权值,令其中最大的为新的父亲。
特别的,如果没有左或右儿子,令其随机权值为-inf
最后就返回新插入的节点的地址就好啦
真的好长啊
remove
删除k,如果当前节点权值小于k,就说明k只可能出现在右儿子中
如果当前节点权值大于k,就说明k只可能出现在左儿子中。
如果当前节点权值就是k……哦找到你咯
如果删除成功就返回false,删除失败就返回true
对于flag为1的情况,删除所有权值为k的数据,对于flag为0的情况,删除一个权值为k的数据
特别的,如果当前节点为空节点,就说明根本没有插入过k,删除失败,返回true
更容易RE了
bool remove(Treap **id,const lld x,const int flag=0)
{
/// remove x from *id
/// if flag remove all the x, else remove one of it
/// return if it fails
if(*id==0) return true;
bool res;
if((*id)->val==x)
{
if(x==0&&(*id)->cnt==0) return true;
if(flag||(*id)->cnt==1)
{
if(x==0||((*id)->lson==0&&(*id)->rson==0))
{
/// x==0 is root, root's cnt can be 0
(*id)->cnt=0;
(*id)->maintain();
return false;
}
Treap **addme=NULL,*me=NULL;
if((*id)->rson)
{
addme=&(*id)->rson;
while((*addme)->lson) addme=&(*addme)->lson;
**id=**addme;
Treap *j=*addme;
*addme=(*addme)->rson;
delete j;
if((*id)->rson) (*id)->rson->maintain(1);
}else
{
me=(*id)->lson;
delete *id;
*id=me;
}
if(*id) (*id)->maintain();
return false;
}else
{
(*id)->cnt--;
(*id)->maintain();
return false;
}
}else
if((*id)->val>x)
{
res=remove(&(*id)->lson,x,flag);
}else
if((*id)->val<x)
{
res=remove(&(*id)->rson,x,flag);
}
(*id)->maintain();
const lld lrd=(*id)->lson? (*id)->lson->rd:-inf,
rrd=(*id)->rson? (*id)->rson->rd:-inf;
if(lrd>rrd&&lrd>(*id)->rd)
{
zag(id);
}else
if(rrd>lrd&&rrd>(*id)->rd)
{
zig(id);
}
return res;
}
我们先讲下边(奸)
如果当前节点不是k,那么就到儿子中处理它
然后删除完之后,是可能改变某些属性的。所以我们先浅维护一下当前节点,
然后在维护一下堆的性质。
最后返回
好了如果k是当前节点呢?
这样只有两种情况:全删和不全删
当且仅当flag=0且cnt大于1时,才不全删。这个时候cnt减一,浅维护当前节点,然后又因为儿子们都没有变,所以直接返回false就可以了
如果是全删,那么要不就是flag为1,要不就是这个节点只有一个。
特别的,如果当前节点是0,而且cnt为0,那么当前树中应该是没有0的,只是一个虚拟根,我们删除的时候要特别处理一下。
回到删除上,如果当前节点是0但是有权值,那我们知道即使全删也不能摧毁当前节点,所以直接cnt化0就好
或者当前节点没有左右儿子,那么删了就删了,不用维护,也是cnt化0
然后浅维护一下当前节点,令其sz[1]为0,等下次维护到他的父节点的时候直接删除
那么其他情况呢?
这里取当前节点的下一个节点(定义为从小到大排序,这个节点的下一个节点)来代替这个节点
为了方便起见,我们不考虑祖先那边有没有下一个节点,我们只考虑本子树内,如果存在这个下一个节点,那么一定是在右子树内。
那么如果没有右子树……我管你有没有左儿子反正就让左儿子来代替俺,就是……直接覆盖掉啦……至于维护大小之类的……回溯到父节点那里会维护的了。
如果有右子树(我才不管你有没有左子树),那么寻找到右子树内的最小值,就是下一个节点,可以证明这个节点没有左儿子(也就是没有比他更小的了),那么就直接让这个节点的右儿子来覆盖掉它。这里注意要用Treap**结构才能保证右儿子取代的是这个节点的地位(比如说父亲的左儿子)
我知道你特别懵(因为我就讲的很迷糊)那么我们明天再讲可以画幅图自己理解消化一下,不急的(我代码就调了三天……qwq所以不推荐大家用指针啦但是指针真的很好用省内存诶嘤)
k-th number
非常著名的寻找第k小(是的到我这里变成了小)问题
如果flag为1,那么计算重复权值。如果flag为0,那么不计算重复权值
同样的,返回找到的节点的指针,失败返回0
什么为之失败呢?比如说访问到空节点啊(就是没有第k号)k为0啊和k大于当前子树大小啊之类的
Treap* kth(Treap *id,const llu k,const int flag=1)
{
/// find the k-th smallest in the root-id
/// return the node address, return NULL when fail
if(id==0) return 0;
if(k==0) return 0;
if(k>id->sz[flag]) return 0;
const llu lsz=id->lson? id->lson->sz[flag]:0;
if(lsz>=k) return kth(id->lson,k,flag);
if(k-lsz<=(flag? id->cnt:min(1ull,id->cnt))) return id;
return kth(id->rson,k-lsz-(flag? id->cnt:min(1ull,id->cnt)),flag);
}
如果左儿子的大小已经比k大,那么说明这棵子树的第k大在左子树,那么直接遍历左子树好了
至于得出是自己那个……理解一下就好我也解释不清
注意就算是flag为0,也有可能id->cnt是0(根节点)这个时候是不能算进去的
然后如果左儿子和自己的总大小加起来都没有k,距离k还差a,那么我们就可以在右儿子里面找第a大,就是整颗子树里面的第k大了
rank
其实和上面那个差不多……先找到这个节点,如果这个节点cnt为0那么返回0(失败)
否则返回1+左儿子大小
lld rank(Treap *id,const lld k,const int flag=1)
{
/// return the rank of node whose val is k-lsz
/// return 0 when fail
if(id==0) return 0;
const llu lsz=id->lson? id->lson->sz[flag]:0;
if(id->val==k)
{
if(id->cnt==0) return 0;
return 1+lsz;
}
if(k<id->val) return rank(id->lson,k,flag);
return rank(id->rson,k,flag)+lsz+(flag? id->cnt:min(1ull,id->cnt));
}
pre&nxt
前驱/后记的定义是在这棵树内不大于/不小于给定数的最大数
所以这个给定的k是有可能在树里找不到的
如果找到了那当然最好
因为我们要找的是不大于/不小于,所以直接返回k就可以了
注意也要判断cnt是否为0
如果找不到怎么办呢?
这里用前驱举例子
Treap* pre(Treap *id,const lld k)
{
/// return the max number which is not greater than k
if(id==0) return 0;
if(id->val==k&&id->cnt) return id;
if(id->val>=k) return pre(id->lson,k);
Treap* p=pre(id->rson,k);
if(p) return p;
if(id->cnt) return id;
return pre(id->lson,k);
}
如果当前节点比k大,明显不满足不大于k,所以我们要到左儿子里面找
那么就只剩下一种情况,就是当前节点权值小于k
这时候就要分类讨论,有可能前驱在右儿子,所以先找一下。右儿子没有再考虑一下自己。如果自己也不行那么才去左儿子找
后继也一样……吧
Treap* nxt(Treap *id,const lld k)
{
/// return the min number which is not less than k
if(id==0) return 0;
if(id->val==k&&id->cnt) return id;
if(id->val<=k) return nxt(id->rson,k);
Treap* p=nxt(id->lson,k);
if(p) return p;
if(id->cnt) return id;
return nxt(id->rson,k);
}
All in all
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <ctime>
namespace ptr_treap
{
typedef long long int lld;
typedef long long unsigned llu;
const lld inf=9223372036854775807;
template <typename T> inline T min(const T a,const T b) { return a<b? a:b; }
class Treap
{
/// use val-0 for val, ltree less than 0, rtree greater than 0
/// cnt0 can be 0, means there is no 0
/// but root should be saved as root
public:
lld val,rd;
llu sz[2],cnt;
// sz[0] for size without repetition
// sz[1] for size with repetition
Treap *lson,*rson;
Treap(const lld=inf);
~Treap(){}
Treap* maintain(const int=0);
void cut();
Treap operator=(const Treap a)
{
val=a.val; cnt=a.cnt; sz[0]=a.sz[0]; sz[1]=a.sz[1]; rd=a.rd;
return *this;
}
operator long long int()const
{
return val;
}
void print(int=0);
};
void Treap::print(int level)
{
printf("%lld\n",val);
if(lson){
for(register int i=0;i<=level;++i) putchar(32); printf("|lson:"); lson->print(level+1);
}
if(rson){
for(register int i=0;i<=level;++i) putchar(32); printf("|rson:"); rson->print(level+1);
}
}
Treap::Treap(const lld r)
{
/// init the node, as a root
val=cnt=sz[0]=sz[1]=0;
lson=rson=0;
rd=r;
}
Treap* Treap::maintain(const int flag)
{
/// update, if flag, update sons first
this->sz[0]=(bool)cnt;
this->sz[1]=cnt;
if(flag)
{
if(lson) lson->maintain(1);
if(rson) rson->maintain(1);
}
if(lson)
{
if(lson->sz[1]) sz[0]+=lson->sz[0], sz[1]+=lson->sz[1];
else delete lson, lson=0;
/// if lson has no size, it is the empty node which has been deleted
}
if(rson)
{
if(rson->sz[1]) sz[0]+=rson->sz[0], sz[1]+=rson->sz[1];
else delete rson, rson=0;
}
return this;
}
void Treap::cut()
{
/// cut the tree
if(lson) lson->cut(), delete lson;
if(rson) rson->cut(), delete rson;
}
void build(Treap **rt)
{
/// build up a new tree
srand(time(0));
if(*rt==0) *rt=new Treap;
return ;
(*rt)->cut();
delete *rt;
*rt=new Treap;
}
Treap* zig(Treap **id)
{
/// left-rotate
if((*id)->val==0) return *id;
Treap *p=*id,*r;
if(p->rson==0) return p;
r=p->rson;
p->rson=r->lson; r->lson=p;
p->maintain();
r->maintain();
return *id=r;
}
Treap* zag(Treap **id)
{
/// right-rotate
if((*id)->val==0) return *id;
Treap *p=*id,*l;
if(p->lson==0) return p;
l=p->lson;
p->lson=l->rson; l->rson=p;
p->maintain();
l->maintain();
return *id=l;
}
Treap* insert(Treap **now,const lld x)
{
/// insert x to the tree whose root is *now
if(*now==0)
{
*now=new Treap(rand());
(*now)->val=x;
(*now)->cnt=1;
return (*now)->maintain();
}
if(x==(*now)->val)
{
(*now)->cnt++;
return (*now)->maintain();
}
Treap *p=NULL;
if(x<(*now)->val)
{
p=insert(&(*now)->lson,x);
}
if(x>(*now)->val)
{
p=insert(&(*now)->rson,x);
}
(*now)->maintain();
const int lrd=(*now)->lson? (*now)->lson->rd:-inf,
rrd=(*now)->rson? (*now)->rson->rd:-inf;
if(lrd>rrd&&lrd>(*now)->rd)
{
zag(now);
}else
if(rrd>lrd&&rrd>(*now)->rd)
{
zig(now);
}
return p;
}
bool remove(Treap **id,const lld x,const int flag=0)
{
/// remove x from *id
/// if flag remove all the x, else remove one of it
/// return if it fails
if(*id==0) return true;
bool res;
if((*id)->val==x)
{
if(x==0&&(*id)->cnt==0) return true;
if(flag||(*id)->cnt==1)
{
if(x==0||((*id)->lson==0&&(*id)->rson==0))
{
/// x==0 is root, root's cnt can be 0
(*id)->cnt=0;
(*id)->maintain();
return false;
}
Treap **addme=NULL,*me=NULL;
if((*id)->rson)
{
addme=&(*id)->rson;
while((*addme)->lson) addme=&(*addme)->lson;
**id=**addme;
Treap *j=*addme;
*addme=(*addme)->rson;
delete j;
if((*id)->rson) (*id)->rson->maintain(1);
}else
{
me=(*id)->lson;
delete *id;
*id=me;
}
if(*id) (*id)->maintain();
return false;
}else
{
(*id)->cnt--;
(*id)->maintain();
return false;
}
}else
if((*id)->val>x)
{
res=remove(&(*id)->lson,x,flag);
}else
if((*id)->val<x)
{
res=remove(&(*id)->rson,x,flag);
}
(*id)->maintain();
const lld lrd=(*id)->lson? (*id)->lson->rd:-inf,
rrd=(*id)->rson? (*id)->rson->rd:-inf;
if(lrd>rrd&&lrd>(*id)->rd)
{
zag(id);
}else
if(rrd>lrd&&rrd>(*id)->rd)
{
zig(id);
}
return res;
}
Treap* kth(Treap *id,const llu k,const int flag=1)
{
/// find the k-th smallest in the root-id
/// return the node address, return NULL when fail
if(id==0) return 0;
if(k==0) return 0;
if(k>id->sz[flag]) return 0;
const llu lsz=id->lson? id->lson->sz[flag]:0;
if(lsz>=k) return kth(id->lson,k,flag);
if(k-lsz<=(flag? id->cnt:min(1ull,id->cnt))) return id;
return kth(id->rson,k-lsz-(flag? id->cnt:min(1ull,id->cnt)),flag);
}
lld rank(Treap *id,const lld k,const int flag=1)
{
/// return the rank of node whose val is k-lsz
/// return 0 when fail
if(id==0) return 0;
const llu lsz=id->lson? id->lson->sz[flag]:0;
if(id->val==k)
{
if(id->cnt==0) return 0;
return 1+lsz;
}
if(k<id->val) return rank(id->lson,k,flag);
return rank(id->rson,k,flag)+lsz+(flag? id->cnt:min(1ull,id->cnt));
}
Treap* pre(Treap *id,const lld k)
{
/// return the max number which is not greater than k
if(id==0) return 0;
if(id->val==k&&id->cnt) return id;
if(id->val>=k) return pre(id->lson,k);
Treap* p=pre(id->rson,k);
if(p) return p;
if(id->cnt) return id;
return pre(id->lson,k);
}
Treap* nxt(Treap *id,const lld k)
{
/// return the min number which is not less than k
if(id==0) return 0;
if(id->val==k&&id->cnt) return id;
if(id->val<=k) return nxt(id->rson,k);
Treap* p=nxt(id->lson,k);
if(p) return p;
if(id->cnt) return id;
return nxt(id->rson,k);
}
}
ptr_treap::Treap *rt;
int opt, x, n;
int main()
{
// freopen("P3369_6.in","r",stdin);
build(&rt);
scanf("%d",&n);
while(n--)
{
scanf("%d%d",&opt,&x);
switch(opt)
{
case 1:
ptr_treap::insert(&rt,x);
break;
case 2:
ptr_treap::remove(&rt,x);
break;
case 3:
printf("%lld\n",ptr_treap::rank(rt,x));
break;
case 4:
printf("%lld\n",(ptr_treap::lld)*ptr_treap::kth(rt,x));
break;
case 5:
printf("%lld\n",(ptr_treap::lld)*ptr_treap::pre(rt,x-1));
break;
case 6:
printf("%lld\n",(ptr_treap::lld)*ptr_treap::nxt(rt,x+1));
break;
default:
break;
}
// rt->print();
}
return 0;
}