非旋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; }