有关Treap,很多博客都给出了详细的讲解,这里就只给出Treap的模板代码和详细的注释,希望大家对Treap有更详细的理解。
模板如下(同时也是BZOJ3224的代码)。
注意在BZOJ和POJ等OJ上提交的时候不要使用srand()函数,否则会RE。
#include<cstdio>
#include<ctime>
#include<algorithm>
using namespace std;
const int maxn=100005;
int n;
struct Node{
int LeftChild,RightChild,v,size,w,rnd;
//w 表示 相同元素个数,v 表示 权值,size 表示 子树大小,rnd 表示随机权值
};
struct Treap{
int root,ans,size;
Node Tree[maxn];
void Update(int k){//更新结点信息
Tree[k].size=Tree[Tree[k].LeftChild].size+Tree[Tree[k].RightChild].size+Tree[k].w;
}
void RTurn(int &k){//旋转操作,不明白画个图理解一下吧.
int t=Tree[k].LeftChild;Tree[k].LeftChild=Tree[t].RightChild;Tree[t].RightChild=k;
Tree[t].size=Tree[k].size;Update(k),k=t;
}
void LTurn(int &k){//旋转操作,不明白画个图理解一下吧.
int t=Tree[k].RightChild;Tree[k].RightChild=Tree[t].LeftChild;Tree[t].LeftChild=k;
Tree[t].size=Tree[k].size;Update(k),k=t;
}
void insert(int &k,int x){//插入操作.
if(k==0){//k==0需新建节点.
size++;k=size;//新建节点.
Tree[k].size=Tree[k].w=1;Tree[k].v=x;Tree[k].rnd=rand();//新节点初始化.
return;
}
Tree[k].size++;//插入k所在的子树中,size++.
if (Tree[k].v==x) Tree[k].w++;
//找到相同键值的节点,键值出现次数+1.
else if (x>Tree[k].v){//仍未找到插入位置,根据性质向左右子查找.
insert(Tree[k].RightChild,x);//插入右子.
if (Tree[Tree[k].RightChild].rnd<Tree[k].rnd) LTurn(k);
//根据随机优先级旋转树来保持堆性质.
//此处维护小根堆,大根堆亦可,将小于号改为大于号即可.
}
else{
insert(Tree[k].LeftChild,x);//插入左子.
if (Tree[Tree[k].LeftChild].rnd<Tree[k].rnd) RTurn(k);
//维护堆性质.
}
}
void erase(int &k,int x){//删除操作.
if (k==0) return ;//未找到待删除节点,返回.
if (Tree[k].v==x){//找到待删除节点.
if (Tree[k].w>1){//若键值出现过多于一次.
Tree[k].w--,Tree[k].size--;//次数-1,子树大小-1.
return ;
}
if (Tree[k].LeftChild*Tree[k].RightChild==0) k=Tree[k].LeftChild+Tree[k].RightChild;
//如果没有儿子或只有一个儿子,那么直接删除.
else if (Tree[Tree[k].LeftChild].rnd<Tree[Tree[k].RightChild].rnd) RTurn(k),erase(k,x);
else LTurn(k),erase(k,x);
//如果有两个儿子,就在两个儿子中选择随机优先级小的那个节点替换当前节点(通过旋转操作).
//再递归删除.
}
else if (x>Tree[k].v) Tree[k].size--,erase(Tree[k].RightChild,x);
else Tree[k].size--,erase(Tree[k].LeftChild,x);
//还没有找到待删除节点,继续根据性质递归查找.
//因为已经确定待删除节点再子树k中,就可以顺便将k的子树大小-1.
}
int kth(int k,int x){//查询第k大.
//此处k为节点编号,x为要查询的值.
if (k==0) return 0;//递归到空节点,直接返回0.
if (x<=Tree[Tree[k].LeftChild].size) return kth(Tree[k].LeftChild,x);
//k小于左子的子树大小,说明第k大在左子树中,查询左子树中的第k大.
else if (x>Tree[Tree[k].LeftChild].size+Tree[k].w) return kth(Tree[k].RightChild,x-Tree[Tree[k].LeftChild].size-Tree[k].w);
//k大于左子的子树大小加本节点键值出现次数,说明第k大在右子中.
//查询右子树中的第k-Tree[Tree[k].LeftChild].size+Tree[k].w(减去左子树的大小和本节点键值出现次数)大.
else return Tree[k].v;
//此时的x>Tree[Tree[k].LeftChild].size且x<=Tree[Tree[k].LeftChild].size+Tree[k].w
//说明当前的节点就是第k大,返回即可.
}
int rank(int k,int x){//查询x的排名(x不一定在Treap中).
if (k==0) return 0;//递归到空节点,直接返回0.
if (Tree[k].v==x) return Tree[Tree[k].LeftChild].size+1;
//x存在于Treap中,且已找到,返回左子树的大小+1即为排名.
else if (x>Tree[k].v) return Tree[Tree[k].LeftChild].size+Tree[k].w+rank(Tree[k].RightChild,x);
//根据性质递归查找.
//在右子中查找,应返回其在右子中的排名+左子的子树大小和本节点键值出现次数.
else return rank(Tree[k].LeftChild,x);
//在左子中查找,返回其在左子中的排名即可.
}
void predecessor(int k,int x){//查询前驱.
if(k==0) return;//递归到空节点,返回.
if(Tree[k].v<x) this->ans=k,predecessor(Tree[k].RightChild,x);
//本节点的键值小于查询值,更新答案,向更大的方向查找(右子).
else predecessor(Tree[k].LeftChild,x);
//本节点的键值大于等于查询值,向更小的方向查找(左子).
}
void successor(int k,int x){//查询后继.
if (k==0) return;//递归到空节点,返回.
if (Tree[k].v>x) this->ans=k,successor(Tree[k].LeftChild,x);
//本节点键值大于查询值,更新答案,向更小的方向查找(左子).
else successor(Tree[k].RightChild,x);
//本节点的键值大于等于查询值,向更大的方向查找(右子).
}
void Insert(int x){insert(root,x);}
void Erase(int x){erase(root,x);}
int Kth(int k){return kth(root,k);}
int Rank(int x){return rank(root,x);}
int Predecessor(int x){predecessor(root,x);return Tree[ans].v;}
int Successor(int x){successor(root,x);return Tree[ans].v;}
}Treap;
int main(){
// freopen("BZOJ3224.in","r",stdin);
// freopen("BZOJ3224.out","w",stdout);
// srand(time(NULL));
scanf("%d",&n);
for (int i=1,opt,x;i<=n;i++){
scanf("%d%d",&opt,&x);
if (opt==1) Treap.Insert(x);
if (opt==2) Treap.Erase(x);
if (opt==3) printf("%d\n",Treap.Rank(x));
if (opt==4) printf("%d\n",Treap.Kth(x));
if (opt==5) printf("%d\n",Treap.Predecessor(x));
if (opt==6) printf("%d\n",Treap.Successor(x));
}
return 0;
}