平衡树 区间树 学习
前记
把之前学的平衡树都写一下,最近更新:Splay
Treap
本人的Treap丢了,放之前学习的Treap看看
#include<bits/stdc++.h>
using namespace std;
struct Treap{
static const int MAXN=400000+10;
static const int INF=2000000000;
struct Node{
int val;
int pri;
int cnt;
int r,l;
int size;
}tree[MAXN];
int root;
int top;
Treap(){
root=0;
top=0;
}
void update(int node){ // 更新 node 的 size 值,相当于 Segment Tree 的 pushup
tree[node].size=tree[tree[node].l].size+tree[tree[node].r].size+tree[node].cnt;
}
void zig(int& node) // 左旋,可以看到会修改 node 的值,因此传入了一个引用
{
int root=tree[node].r;
tree[node].r=tree[root].l;
tree[root].l=node;
update(node);
update(root);
node=root;
}
void zag(int& node) // 右旋
{
int root=tree[node].l;
tree[node].l=tree[root].r;
tree[root].r=node;
update(node);
update(root);
node=root;
}
void insert(int& node,int x) // 在 node 中插入 x
{
if(!node){ //新建结点
node=++top; //深度++,结点编号
tree[node].pri=rand(); //随机优先值
tree[node].val=x; //节点值
tree[node].size=0; //子数大小
tree[node].cnt=0; //计相同数的个数
tree[node].l=tree[node].r=0; //初始化左、右子数大小
}
tree[node].size++;
if(tree[node].val==x){ //发现相同结点
tree[node].cnt++; //相同结点数++
}
else
if(tree[node].val>x) //往左子树走
{
insert(tree[node].l,x);
if(tree[node].pri>tree[tree[node].l].pri) //按优先级旋转treap
zag(node);//右旋
}
else //往右子树走
{
insert(tree[node].r,x);
if(tree[node].pri>tree[tree[node].r].pri) //按优先级旋转treap
zig(node);//左旋
}
//update(node); //更新
}
bool erase(int& node,int x) //删除 x
{
if(!node)//空就返回
return false;
if(tree[node].val==x) //删除
{
tree[node].size--;
if(tree[node].cnt>1){
tree[node].cnt--;
}
else
if(tree[node].l==0||tree[node].r==0){//更新结点
node=tree[node].l+tree[node].r;
}
else
if(tree[tree[node].l].pri<tree[tree[node].r].pri)
{//按优先级旋转treap
zag(node);
erase(node,x); //更新旋转后结点
}
else
{
zig(node);
erase(node,x); //更新旋转后结点
}
return true;
}
else //往下找结点
{
bool flag;
if(tree[node].val>x){
flag=erase(tree[node].l,x);
}
else{
flag=erase(tree[node].r,x);
}
if(flag)
tree[node].size--;
return flag;
}
}
int rank(int node,int x) //查询排名为xx的数, 有多少个数比x小,记得加1
{
if(tree[node].val==x){ //找到返回左子树大小 ,左子数的所以结点必定小于根和右子树结点(BST性质
return tree[tree[node].l].size;
}
else if(tree[node].val<x){ //递归右子树找点,更新排名
return tree[tree[node].l].size+rank(tree[node].r,x)+tree[node].cnt;
}
else{
return rank(tree[node].l,x); //递归左子树找点,更新排名
}
}
int k_th(int node,int k) // 第 k 小数
{
if(k<=tree[tree[node].l].size)
return k_th(tree[node].l,k);
k-=tree[tree[node].l].size+tree[node].cnt;
if(k<=0)
return tree[node].val;
else
return k_th(tree[node].r,k);
}
int pre(int x) // 前驱,即最接近 x 但又比它小的数
{
int max1=-INF,node=root;
while(node)
{
if(tree[node].val<x){ // 如果该结点值小于x,更新最大值,往右继续找(往大的找
max1=max(max1,tree[node].val);
node=tree[node].r;
}
else // 否则往左找(往小的找
node=tree[node].l;
}
return max1;
}
int suf(int x) // 后继,代码很接近前驱
{
int min1=INF,node=root;
while(node){
if(tree[node].val>x){ // 如果该结点值大于x,更新最小值,往左继续找(往小的找
min1=min(min1,tree[node].val);
node=tree[node].l;
}
else
node=tree[node].r; // 否则往右找(往大的找
}
return min1;
}
// 遍历整棵树,输出
// 输出格式为 “节点值(x节点个数)”
void print(int node,int level){
if(!node) return;
print(tree[node].l,level+1);
cout<<tree[node].val<<" (x"<<tree[node].cnt<<"), ";
print(tree[node].r,level+1);
cout<<endl;
}
}T;
int main(){
srand(time(0));
// 命令的含义应该能看懂吧
// 命令必须合法,没加不合法鲁棒
// 不合法命令可能会出现 Runtime Error 或者魔改 Treap
// 命令的格式是:“命令 值”
//cout<<"Commands list: del / ins / pre / suf / rnk / kth (Maybe error)"<<endl;
int G;
ios::sync_with_stdio(0);
cin>>G;
while(G--)
{
//cout<<"Print: ";
//T.print(T.root,1); 遍历整棵树,输出
string m;
int x;
cin>>m>>x;
if(m=="del"||m=="2")
T.erase(T.root,x);
else
if(m=="ins"||m=="1")
T.insert(T.root,x);
else
if(m=="pre"||m=="5")
cout<<T.pre(x)<<endl;
else
if(m=="suf"||m=="6")
cout<<T.suf(x)<<endl;
else
if(m=="rnk"||m=="3")
cout<<T.rank(T.root,x)+1<<endl;
else
if(m=="kth"||m=="4")
cout<<T.k_th(T.root,x)<<endl;
}
return 0;
}
FHQ-Treap
//FHQtreap
#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
int n,x,y,mode,cnt,root;
struct kkk{
int size,val,pri,l,r;
}tree[maxn];
////////////////////////////////////////////////////////////////////////////
int New(int val){
tree[++cnt].size=1;
tree[cnt].val=val;
tree[cnt].pri=rand();
tree[cnt].l=tree[cnt].r=0;
return cnt;
}
void update(int node){
tree[node].size=tree[tree[node].l].size+tree[tree[node].r].size+1;
}
void spilt(int node,int &x,int &y,int val){
if(node==0){
x=y=0;return;
}
if(tree[node].val<=val){
x=node;spilt(tree[node].r,tree[x].r,y,val);
}else{
y=node;spilt(tree[node].l,x,tree[y].l,val);
}
update(node);
}
int merge(int x,int y){
if(!x||!y)return x+y;
if(tree[x].pri<tree[y].pri){
tree[x].r=merge(tree[x].r,y);
update(x);
return x;
}else{
tree[y].l=merge(x,tree[y].l);
update(y);
return y;
}
}
void find(int node,int val){
while(tree[tree[node].l].size+1!=val){
if(tree[tree[node].l].size>=val)
node=tree[node].l;
else
val-=tree[tree[node].l].size+1,node=tree[node].r;
}
printf("%d\n",tree[node].val);
}
////////////////////////////////////////////////////////////////////////////
void insert(int val){
int x=0,y=0,z=0;
z=New(val);
spilt(root,x,y,val);
x=merge(x,z);
root=merge(x,y);
}
void del(int val){
int x=0,y=0,z=0;
spilt(root,x,y,val);
spilt(x,x,z,val-1);
z=merge(tree[z].l,tree[z].r);
x=merge(x,z);
root=merge(x,y);
}
void rank(int val){
int x=0,y=0;
spilt(root,x,y,val-1);
printf("%d\n",tree[x].size+1);
root=merge(x,y);
}
void pre(int val){
int x=0,y=0;
spilt(root,x,y,val-1);
find(x,tree[x].size);
root=merge(x,y);
}
void suf(int val){
int x=0,y=0;
spilt(root,x,y,val);
find(y,1);
root=merge(x,y);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&mode,&x);
if(mode==1)insert(x);
if(mode==2)del(x);
if(mode==3)rank(x);
if(mode==4)find(root,x);
if(mode==5)pre(x);
if(mode==6)suf(x);
}
}
FHQ-Treap 文艺平衡树
//FHQtreap
#include<bits/stdc++.h>
#define maxn 1000001
using namespace std;
int n,m,x,y,mode,cnt,root,tag[maxn];
struct kkk{
int size,val,pri,l,r;
}tree[maxn];
////////////////////////////////////////////////////////////////////////////
int New(int val){
tree[++cnt].size=1;
tree[cnt].val=val;
tree[cnt].pri=rand();
tree[cnt].l=tree[cnt].r=0;
return cnt;
}
void pushdown(int x){
tag[x]^=1;
swap(tree[x].l,tree[x].r);
tag[tree[x].l]^=1;tag[tree[x].r]^=1;
}
void update(int node){
tree[node].size=tree[tree[node].l].size+tree[tree[node].r].size+1;
}
void spilt(int node,int &x,int &y,int val){
if(node==0){
x=y=0;return;
}
if(tag[node])pushdown(node);
if(val>tree[tree[node].l].size){
x=node;spilt(tree[node].r,tree[node].r,y,val-tree[tree[node].l].size-1);
}else{
y=node;spilt(tree[node].l,x,tree[node].l,val);
}
update(node);
}
int merge(int x,int y){
if(!x||!y)return x+y;
if(tag[x])pushdown(x);
if(tag[y])pushdown(y);
if(tree[x].pri<tree[y].pri){
tree[x].r=merge(tree[x].r,y);
update(x);
return x;
}else{
tree[y].l=merge(x,tree[y].l);
update(y);
return y;
}
}
////////////////////////////////////////////////////////////////////////////
void insert(int val){
int x=0,y=0,z=0;
z=New(val);
spilt(root,x,y,val);
x=merge(x,z);
root=merge(x,y);
}
void rotate(int x,int y){
int a,b,c,d;
spilt(root,a,b,y+1);
spilt(a,c,d,x);
tag[d]^=1;
root=merge(merge(c,d),b);
}
void print(int x){
if(tag[x])pushdown(x);
if(tree[x].l)print(tree[x].l);
printf("%d ",tree[x].val);
if(tree[x].r)print(tree[x].r);
}
int main(){
//srand(time(0));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)insert(i);
//print(root);cout<<endl;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
rotate(x-1,y-1);
//cout<<endl;print(root);cout<<endl;
}
print(root);
}
IST 文艺平衡树
#include<bits/stdc++.h>
#define maxn 1000001 //不知道为什么我就是喜欢开怎么大,其实完全没必要
using namespace std;
struct kkk{
int l,r,tag,x,size;
}tree[maxn];
int t[maxn],Left[maxn],middle[maxn],Right[maxn],rub[maxn],tmp[maxn];
int n,m,x,y,tot,top,le,ri,mi,root;
inline int read(){ //快读,形成好习惯
int s=0,w=1;
char ch=getchar();
while(ch<='0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int New(){ //新建节点
int pos=rub[tot--];
tree[pos].l=tree[pos].r=tree[pos].size=tree[pos].x=tree[pos].tag=0;
return pos;
}
void pushdown(int node){ //pushdown
if(tree[node].tag){ //如果有标记就操作
tree[node].tag=0; //原标记清0,和线段树一样
tree[tree[node].l].tag^=1; //左标记取相反值,就是0变成1,1变成0
swap(tree[tree[node].l].l,tree[tree[node].l].r); //交换左子树的左右孩子,因为要翻转嘛
tree[tree[node].r].tag^=1; //右标记取相反值
swap(tree[tree[node].r].l,tree[tree[node].r].r); //交换右子树的左右孩子
}
}
void pushup(int node){ //pushup
tree[node].size=tree[tree[node].l].size+tree[tree[node].r].size;
}
void spilt(int node,int begin,int end,int x,int y){ //分裂
if(end<x){Left[++le]=node;return;} //如果查询区间在要修改区间左部,并入左数组
if(x<=begin&&end<=y){middle[++mi]=node;return;} //如果查询区间在要修改区间中,并入中数组
if(y<begin){Right[++ri]=node;return;} //如果查询区间在要修改区间右部,并入右数组
pushdown(node); //pushdown更新tag标记
int mid=begin+tree[tree[node].l].size-1; //注意,因为经过修改后的树不像线段树那么平衡,所以mid值有所变动
spilt(tree[node].l,begin,mid,x,y); //向左子树递归分裂
spilt(tree[node].r,mid+1,end,x,y); //向左子树递归分裂
rub[++tot]=node; //这里是垃圾回收
}
void marge(){ //合并
int cnt=0; //记得数组清0,要按顺序合并哦
for(int i=1;i<=le;++i)t[++cnt]=Left[i]; //把左部分合并
for(int i=1;i<=mi;++i)t[++cnt]=middle[i]; //把中部分合并
for(int i=1;i<=ri;++i)t[++cnt]=Right[i]; //把右部分合并
while(cnt>1){ //重新建树过程
int mid=(cnt+1)/2; //取mid值
for(int i=1;i<=mid;++i){ //将线段树底部节点一一合并
int x=t[i*2-1],y=t[i*2];
if(!x||!y)t[i]=x+y; //如果有一个是空,特判
else{
int node=New(); //新建一个节点
tree[node].l=x;tree[node].r=y; //新节点指儿子
pushup(node); //pushup
t[i]=node; //把新节点并入数组里
}
}
for(int i=mid+1;i<=cnt;++i)t[i]=0;//记得把剩下节点清0,原因是他们已经在上面合并过了,而且避免了奇偶的错误
cnt=(cnt+1)/2; //到下一层继续建树
}root=t[1]; //根更新
}
void build(int node,int l,int r){ //建树
if(l==r){
tree[node].x=l; //初值是i嘛
tree[node].size=1; //树大小是1
return;
}int mid=(l+r)>>1; //和线段树一毛一样
tree[node].l=++top; //新建左子树的编号
build(top,l,mid); //递归左子树建树
tree[node].r=++top; //新建右子树的编号
build(top,mid+1,r); //递归右子树建树
pushup(node); //pushup
}
void rotate(int x,int y){ //翻转
le=0,ri=0,mi=0; //记得左,中,右部分数组要清0
spilt(root,1,tree[root].size,x,y); //分裂
for(int i=1;i<=mi;++i)tmp[i]=middle[i]; //把中部分提取出来
for(int i=1;i<=mi;++i){
middle[i]=tmp[mi-i+1]; //翻过来,就是直接赋值嘛
tree[middle[i]].tag^=1; //打标记
swap(tree[middle[i]].l,tree[middle[i]].r); //交换左右子树
}marge(); //合并
}
void print(int node){ //输出
pushdown(node); //pushdown
if(tree[node].size==1){
printf("%d ",tree[node].x); //输出
return ;
}print(tree[node].l);print(tree[node].r); //递归左右子树
}
int main(){
n=read();m=read();root=top=1; //记得赋初值
build(1,1,n); //建树
for(int i=1;i<=m;++i){
x=read();y=read();
rotate(x,y); //翻转
}print(root); //输出
}
替罪羊树
思想:当平衡树不平衡时就重构
//替罪羊树 P3369 【模板】普通平衡树
#include<bits/stdc++.h>
#define maxn 1000000 //数组大小
#define alpha 0.75 //替罪羊常数
#define L(x) tree[x].l //左儿子
#define R(x) tree[x].r //右儿子
#define F(x) tree[x].fa //父亲
using namespace std;
struct Node{
int l,r,val,fa,size,sum,cnt;
// l 表示 左儿子 , r 表示 右儿子 ,val 表示 值 , fa 表示 父亲
// size 表示 子树节点和 , cnt 表示 相同val的数量 , sum 表示 子树所有 cnt 的和
}tree[maxn],seq[maxn];
int n,len,tot,flag,root,rub[maxn],cnt,mode,x;
int rublish(){ //垃圾回收,省空间
if(tot>0)return rub[tot--]; //如果垃圾数组有不用的编号就拿来用
return ++len; //没有就开一个新编号
}
void New(int val,int node,int fa){ //新建节点 val 为值, node 是编号 , fa 表示 node 的父亲
tree[node].val=val;tree[node].fa=fa;
tree[node].size=tree[node].cnt=tree[node].sum=1;
tree[node].l=tree[node].r=0;
}
int Find(int x,int node){ //寻找 x 值 的编号
if(x<tree[node].val&&tree[node].l)return Find(x,tree[node].l);
if(x>tree[node].val&&tree[node].r)return Find(x,tree[node].r);
return node;
}
void update(int node,int x,int y){ //更新有修改信息的点
if(!node)return ; //如果跳过根了就结束
tree[node].size+=x;
tree[node].sum+=y;
if((double)tree[node].size*alpha<(double) //随便判要不要重构
max(tree[L(node)].size,tree[R(node)].size))
{flag=node;} //记录要重构的树根
update(tree[node].fa,x,y);
}//重建树begin
void dfs(int node){ //dfs把树拍扁成序列
if(node==0)return;
dfs(L(node)); //先左
seq[++cnt]=tree[node]; //其实只需要记录部分信息,不过我太懒了
rub[++tot]=node; //扔进垃圾筒
dfs(R(node)); //后右
}
int Rebuilding_Tree(int l,int r,int fa){ //把序列建为完全二叉树
if(l>r)return 0; //如果是空就返回
int mid=(l+r)>>1,node=rublish(); //取一个编号
tree[node].fa=fa;tree[node].cnt=seq[mid].cnt;tree[node].val=seq[mid].val; //基本信息赋值
tree[node].l=Rebuilding_Tree(l,mid-1,node);tree[node].r=Rebuilding_Tree(mid+1,r,node); //左右儿子重建
tree[node].sum=tree[L(node)].sum+tree[R(node)].sum+seq[mid].cnt;tree[node].size=r-l+1; //信息更新
return node; //返回根
}
void rebuild(int node){ //重建树的主函数
cnt=0;dfs(node);
if(node==root)root=Rebuilding_Tree(1,cnt,0); //如果根重构就特判
else{
if(L(F(node))==node)tree[F(node)].l=Rebuilding_Tree(1,cnt,F(node)); //如果原来是左儿子就放左边
else tree[F(node)].r=Rebuilding_Tree(1,cnt,F(node)); //不然就放右边
}
}//重建树end
void insert(int x){ //插入
if(root==0){root=rublish();New(x,root,0);return;} //根插入特判
int node=Find(x,root); //找到那个点
if(x==tree[node].val){ //如果之前就有就把数量加1
tree[node].cnt++;
update(node,0,1); //更新
}else{
if(x<tree[node].val){ //没有就看是放左边还是右边
tree[node].l=rublish(); //取一个编号
New(x,tree[node].l,node); //新建节点
}else{
tree[node].r=rublish(); //取一个编号
New(x,tree[node].r,node); //新建节点
}
update(node,1,1); //更新
}
}
void del(int x){ //删除
int node=Find(x,root);tree[node].cnt--; //直接找到节点然后数量减1
update(node,0,-1); //更新
}
int Rank(int x){ //查询排名,和BST一样
int node=root,ans=0;
while(tree[node].val!=x){
if(x<tree[node].val)node=tree[node].l; //往左找
else ans+=tree[L(node)].sum+tree[node].cnt,node=tree[node].r; //累加进答案往右找
if(node==0){break;} //找不到就退出
}ans+=tree[L(node)].sum; //加上左子树的数量
return ans+1; //加1
}
int kth(int x){ //查询排名x的树,和BST一样
int node=root;
while(1){
if(x<=tree[L(node)].sum)node=L(node); //往左找
else{
x-=tree[L(node)].sum; //减去左边的数量
if(x<=tree[node].cnt){ //如果在该节点中
return tree[node].val; //返回值
}
x-=tree[node].cnt; //减去自己的值
node=R(node); //往右节点跑
}if(node==0)return 0; //找不到就返回
}
}
int pre(int x){ //查询前驱
int node=Rank(x);node--; //找到x的排名,减1就是前驱的排名
return kth(node);
}
int suf(int x){ //查询后继
int node=Rank(x);int y=Find(x,root); //找到x的排名,加上自己的数量就是后继的排名
if(tree[y].val==x)node+=tree[y].cnt;
return kth(node);
}
int main(){ //主函数
scanf("%d",&n);
for(int i=1;i<=n;i++){flag=-1; //重构flag赋初值
scanf("%d%d",&mode,&x);
if(mode==1){insert(x);}
if(mode==2){del(x);}
if(mode==3){printf("%d\n",Rank(x));}
if(mode==4){printf("%d\n",kth(x));}
if(mode==5){printf("%d\n",pre(x));}
if(mode==6){printf("%d\n",suf(x));}
if(flag!=-1)rebuild(flag);
}
}
Splay
我tm终于学Splay啦
#include<bits/stdc++.h>
#define inf 2147483647
#define maxn 1000001
#define L(node) tree[node].ch[0] //左儿子
#define R(node) tree[node].ch[1] //右儿子
#define F(node) tree[node].fa //父亲
#define compare(node,x) x>tree[node].val//比较x是node的左儿子还是右儿子
using namespace std;
struct Node{
int ch[3],fa,cnt,sum,val;
}tree[maxn];
int root,mode,x,len,n;
int read(){ //快读
int f=0,o=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')o=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){f=f*10+ch-'0';ch=getchar();}
return o*f;
}
void pushup(int node){ //pushup 更新数值个数
tree[node].sum=tree[L(node)].sum+tree[R(node)].sum+tree[node].cnt;
}
void New(int node,int x,int fa){ //新建节点
tree[node].ch[0]=tree[node].ch[1]=0;
tree[node].fa=fa;tree[node].val=x;
tree[node].cnt=tree[node].sum=1;
}
void ratate(int node){
int fa=tree[node].fa; //node节点的父亲
int gfa=tree[fa].fa; //node节点的爷爷
int z=compare(fa,tree[node].val); //判断 node是 fa 的左还是右孩子
tree[gfa].ch[compare(gfa,tree[fa].val)]=node; //将node变到爷爷的某个孩子上
tree[node].fa=gfa; //指认父亲
tree[fa].ch[z]=tree[node].ch[z^1]; //node原来在父亲的方向变为原node的反方向节点
tree[tree[node].ch[z^1]].fa=fa; //原node的反方向节点指认父亲
tree[node].ch[z^1]=fa; //node的反方向就是fa
tree[fa].fa=node; //指认父亲
pushup(fa);pushup(node); //更新,因为fa在下面所以先更新fa
}
//1.node变到原来fa的位置
//2.fa变成了 node原来在fa的 相对的那个儿子
//3.fa的非node的儿子不变,node的 node原来在fa的 那个儿子不变
//4.node的 node原来在fa的 相对的 那个儿子 变成了 fa原来是node的那个儿子
void Splay(int node,int goal){ //把node旋转到goal上
while(tree[node].fa!=goal){ //父亲是目标就可以退出了
int fa=tree[node].fa;
int gfa=tree[fa].fa;
//cout<<node<<' '<<fa<<' '<<gfa<<endl;
if(gfa!=goal){ //如果爷爷不是目标就不会执行双旋
(compare(fa,tree[node].val))!=(compare(gfa,tree[fa].val)) //如果fa和gfa和node不在一条链上
?ratate(node) //将自己旋上去
:ratate(fa); //不然就先旋父亲
}
ratate(node); //单旋
} if(!goal)root=node;
}
void Find(int x){ //查找x的位置,并把它Splay到根
int node=root;if(!node)return;
while(x!=tree[node].val){
if(x<tree[node].val&&L(node))node=L(node);else
if(x>tree[node].val&&R(node))node=R(node);else
break;
} Splay(node,0); //旋到根
}
int Next(int x,int mode){ //查找x的 前驱 0/后继 1
Find(x);int node=root;
if((tree[node].val>x&&mode==1)||(tree[node].val<x&&mode==0))return node;
node=tree[node].ch[mode];
while(tree[node].ch[mode^1])node=tree[node].ch[mode^1];
return node;
}
void insert(int x){ //插入
int node=root,fa=0;
while(tree[node].val!=x&&node){
fa=node; //记录父亲
node=tree[node].ch[compare(node,x)]; //向下找点
}
if(node)tree[node].cnt++; //如果原来有该节点就累加数量
else{
node=++len;
if(fa)
tree[fa].ch[compare(fa,x)]=node; //父亲认儿子
New(node,x,fa); //新建节点
}
Splay(node,0); //旋到根
}
void del(int x){ //删除节点
int last=Next(x,0); //查前驱
int nxt =Next(x,1); //查后继
Splay(last,0);Splay(nxt,last);
int node=tree[nxt].ch[0];
if(tree[node].cnt>1){
tree[node].cnt--; //减去个数
Splay(node,0);
}else
tree[nxt].ch[0]=0; //直接删除
}
int kth(int x){ //查询排名x的树,和BST一样
int node=root;
while(1){
if(x<=tree[L(node)].sum)node=L(node); //往左找
else{
x-=tree[L(node)].sum; //减去左边的数量
if(x<=tree[node].cnt)return tree[node].val; //如果在该节点中,就返回值
x-=tree[node].cnt; //减去自己的值
node=R(node); //往右节点跑
}if(node==0)return 0; //找不到就返回
}
}
int Rank(int x){Find(x); return tree[L(root)].sum;} //找Rank
int pre(int x){return tree[Next(x,0)].val;} //前驱
int suf(int x){return tree[Next(x,1)].val;} //后继
int main(){ //主函数
insert(-inf);insert(inf);
n=read();
for(int i=1;i<=n;i++){
mode=read();x=read();
if(mode==1)insert(x);
if(mode==2)del(x);
if(mode==3)printf("%d\n",Rank(x));
if(mode==4)printf("%d\n",kth(x+1));
if(mode==5)printf("%d\n",pre(x));
if(mode==6)printf("%d\n",suf(x));
}
}
点赞嗷 ~ \ QwQ / *