以前写线段树的时候就天天叫代码多,今天写了平衡树,呵呵。。。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int N=1e6,inf=0x3f3f3f3f;
struct node
{
int r;//随机的优先级
int size,val,cnt;//val为节点的权值,cnt为相同权值节点合并后的个数
int ch[2];//ch[0]为左儿子,ch[1]为右儿子
}tree[N];
int n,rt,tot;
void rotate(int &u,int d)//d=0为左旋,d=1为右旋
{
int v=tree[u].ch[!d];
//调整父子关系
tree[u].ch[!d]=tree[v].ch[d];
tree[v].ch[d]=u;
//更新节点信息
//u现在为v的儿子,必须先更新u的信息,才能正确更新v的信息
tree[u].size=tree[tree[u].ch[0]].size+tree[tree[u].ch[1]].size+tree[u].cnt;
tree[v].size=tree[tree[v].ch[0]].size+tree[tree[v].ch[1]].size+tree[v].cnt;
//返回v的编号,因为当前访问的已经是v了
u=v;
}
void insert(int &k,int x)
{
if(k==0)//如果当前节点为空,就新建节点
{
k=++tot;
tree[k].r=rand();
tree[k].val=x;
tree[k].cnt=tree[k].size=1;
return;
}
if(tree[k].val==x) {tree[k].cnt++,tree[k].size++;return;}//如果已有权值相等的节点,个数+1
int d=x<tree[k].val?0:1;//小于就进入左子树,大于则进入右子树
insert(tree[k].ch[d],x);
if(tree[tree[k].ch[d]].r<tree[k].r) rotate(k,!d);
//如果不满足小根堆性质,将u的儿子节点v旋上来
//旋转前,k中的值为u(注意k储存的无论为何值,其意义都为当前函数正在访问的节点的编号;
//而k中的某个具体的值u只表示treap树中的任一节点)
//如果u这个节点被旋下去,则当前函数访问的节点就变成了v,则k会被赋值为旋上来的节点v
//当函数结束后,k中的值会因为取地址符&而返回至上一个函数的ch[d]中
//这样来实现对于原本u的父亲(即原本v的爷爷)的儿子指针的更改
tree[k].size=tree[tree[k].ch[0]].size+tree[tree[k].ch[1]].size+tree[k].cnt;//更新节点信息
}
void remove(int &k,int x)
{
if(k==0) return;//如果当前节点为空,返回
if(tree[k].val==x)
{
if(tree[k].cnt==1)//如果该权值的节点只有一个
{
if(tree[k].ch[0]==0||tree[k].ch[1]==0)//如果没有儿子,或只有一个儿子
k=tree[k].ch[tree[k].ch[0]?0:1];//将儿子提上来
else//否则将左右儿子中随机优先级小的节点提上来
{
int d=tree[tree[k].ch[0]].r<tree[tree[k].ch[1]].r?0:1;
rotate(k,!d);
remove(tree[k].ch[!d],x);//继续向下递归
}
}
else tree[k].cnt--;//否则个数减1
}
else remove(tree[k].ch[x<tree[k].val?0:1],x);//否则继续递归
tree[k].size=tree[tree[k].ch[0]].size+tree[tree[k].ch[1]].size+tree[k].cnt;//更新节点信息
}
int get_rank(int k,int x)
{
if(x==tree[k].val) return tree[tree[k].ch[0]].size+1;//如果当前节点就是x
if(x<tree[k].val) return get_rank(tree[k].ch[0],x);//如果x在左子树中
//如果x在右子树中,要加上左子树大小和当前节点的个数
if(x>tree[k].val) return tree[tree[k].ch[0]].size+tree[k].cnt+get_rank(tree[k].ch[1],x);
}
int get_val(int k,int x)
{
if(x<=tree[tree[k].ch[0]].size) return get_val(tree[k].ch[0],x);//如果在左子树中
else if(x<=tree[tree[k].ch[0]].size+tree[k].cnt) return tree[k].val;//如果cnt个当前节点中排名包括x
//如果在右子树中,查找右子树中排名为(x-左子树大小-当前节点个数)的节点
else return get_val(tree[k].ch[1],x-tree[tree[k].ch[0]].size-tree[k].cnt);
}
int get_pre(int k,int x)
{
if(k==0) return -inf;//如果节点为空返回负无穷,避免干扰其他答案
//如果当前节点的值大于等于x,向左子树寻找更小的值
if(x<=tree[k].val) return get_pre(tree[k].ch[0],x);
//如果当前节点的值小于x,向右子树寻找最大的值
if(tree[k].val<x) return max(tree[k].val,get_pre(tree[k].ch[1],x));
}
int get_nxt(int k,int x)
{
if(k==0) return inf;//如果节点为空返回正无穷,避免干扰其他答案
//如果当前节点的值小于等于x,向右子树寻找更大的值
if(tree[k].val<=x) return get_nxt(tree[k].ch[1],x);
//如果当前节点的值大于x,向左子树寻找最小的值
if(x<tree[k].val) return min(tree[k].val,get_nxt(tree[k].ch[0],x));
}
int main()
{
scanf("%d",&n);
for(int i=1,op,x;i<=n;i++)
{
scanf("%d%d",&op,&x);
if(op==1) insert(rt,x);
if(op==2) remove(rt,x);
if(op==3) printf("%d\n",get_rank(rt,x));
if(op==4) printf("%d\n",get_val(rt,x));
if(op==5) printf("%d\n",get_pre(rt,x));
if(op==6) printf("%d\n",get_nxt(rt,x));
}
return 0;
}