点击查看代码
//此代码中并未对重复元素的数量进行维护而是在树的节点存在相同的元素
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
struct node{
int ls,rs;//左右子树
int val,rnd;//val代表权值 rnd代表优先级
int size;//size代表以它为根节点的节点数量
}tr[N];
int New(int v)//新建节点
{
tr[++tot].val=v;
tr[tot].rnd=rand();
tr[tot].size=1;
return tot;
}
void insert(int v)//插入节点操作
{
int x,y;
//先将树拆分为小于v的部分和大于v的部分,再进行新建一个节点再将三部分合并
split(root,v,x,y);
root=merge(mege(x,New(v)),y);
}
void Delete(int v)//删除操作
{
int x,y,z;
//将权值为v的节点删除
split(root,v,x,z);
split(x,v-1,x,y);
y=merge(tr[y].ls,tr[y].rs);//可以理解为删除节点操作
root=merge(merge(x,y),z);
}
int find_kth(int u,int k)//查询排名为k的节点
{
int cnt=0;
while(1)
{
if(k<=tr[tr[u].ls].size) u=tr[u].ls;//说明排名为k的节点在左子树上
else if(k==tr[tr[u].ls].size+1) return u;//说明此节点即为所求
else k-=tr[tr[u].ls].size+1,u=tr[u].rs;//说明排名为k的节点在右子树上,并减去左子树的数量
}
}
int find_prev(int v)//查询前趋
{
int x,y;
split(root,v-1,x,y);//将树分成小于v的部分和大于v的部分
//其中左子树中最大的数就是前趋了
int tmp=tr[find_kth(x,tr[x].size)].val;//寻找第tr[x].size大的数
root=merge(x,y);//查询完后进行合并
return tmp;
}
int find_next(int v)//查询后继
{
//与查询前趋同理
int x,y;
split(root,v,x,y);
int tmp=tr[find_kth(y,1)].val;
root=merge(x,y);
return tmp;
}
void split(int now,int v,int &x,int &y)//分裂操作
{
//&x &y 代表当前分裂出的两棵子树的虚拟节点
if(!now) return x=y=0,void();//当前节点为空,设为虚拟节点
if(tr[now].val<=v) x=now,split(tr[now].rs,v,rs[now].rs,y);//当前权值小于v(即分裂权值)左子树全部合并到左区间树,左区间树建立右虚拟节点 ,再向右子树递归
else y=now,split(tr[now].ls,v,x,tr[now].ls);//与上同理
pushup(now);
}
int merge(int u,int v)//合并操作
{
if(!u ||!v) return u|v;
//左边的优先级高,拿左边的右儿子与右边的树进行合并
if(tr[u].rnd < tr[v].rnd ) tr[u].rs=merge(tr[u].rs,v),pushup(u),return u;
//与上同理
else tr[v].ls=merge(u,tr[v].ls),pushup(v),return v;
}