【模板】Treap (模板题:洛谷P3369普通平衡树)

题目描述

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:

  1. 插入x数

  2. 删除x数(若有多个相同的数,因只删除一个)

  3. 查询x数的排名(若有多个相同的数,因输出最小的排名)

  4. 查询排名为x的数

  5. 求x的前驱(前驱定义为小于x,且最大的数)

  6. 求x的后继(后继定义为大于x,且最小的数)

输入输出格式

输入格式:

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

输出格式:

对于操作3,4,5,6每行输出一个数,表示对应答案

输入输出样例

输入样例#1:
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
输出样例#1:
106465
84185
492737










说明

时空限制:1000ms,128M

1.n的数据范围:n<=100000

2.每个数的数据范围:[-1e7,1e7]




#include
#include
#include
using namespace std;

struct TREAP{
	int l,r,val,sz,recy,rd;
	//sz表示树的节点数,recy记录自己被重复了几次
	//rd表示该节点的优先级 
}t[1000000];

int m,size,root,ans;


void update(int k){
	t[k].sz=t[t[k].l].sz+t[t[k].r].sz+t[k].recy;
	//更新维护 
}

void left_rotate(int &k){
	int y=t[k].r;t[k].r=t[y].l;t[y].l=k;
	t[y].sz=t[k].sz;update(k);k=y;
	//左旋,至于这里的k=y,由于下面的递归调用,
	//它会一直迭代,所以无需担心会有什么错误 
}
void right_rotate(int &k){
	int y=t[k].l;t[k].l=t[y].r;t[y].r=k;
	t[y].sz=t[k].sz;update(k);k=y;
	//右旋 
}


//以下函数的调用中(int k)表示在根为k的子树中

void insert(int &k,int x){//插入操作 
	if (k==0){//无节点时特判,
	          //或是递归的边界,即插入叶节点 
		++size;k=size;t[k].sz=t[k].recy=1;
		t[k].val=x;t[k].rd=rand();return ;
		  //rand()生成随机的优先级,保证了期望复杂度 
	}
	++t[k].sz;//每次向下找同时增加该节点1个节点数
	if (t[k].val==x) ++t[k].recy;
	          //如果是相同数字,只需++recy即可 
	else if (x>t[k].val){
		insert(t[k].r,x);
		if (t[t[k].r].rd1){
			--t[k].recy;--t[k].sz;return ;
			//如果重复了,只需--recy即可 
		}
		if (t[k].l*t[k].r==0) k=t[k].l+t[k].r;
		    	//如果左右儿子有为空的情况
			//或将其变为其儿子节点,或将其删除 
		else if (t[t[k].l].rd	//如果其左右儿子都有,选择优先级较大的,
			//保持以后的堆性质,同时将k节点下沉 
		       else left_rotate(k),del(k,x);
	}
	else if (x>t[k].val)
				--t[k].sz,del(t[k].r,x);
			//如果关键值不同,继续向下找 
		   else --t[k].sz,del(t[k].l,x);
}


int atrank(int k,int x){//寻找值为x的数的排名 
	if (k==0) return 0;
	if (t[k].val==x) return t[t[k].l].sz+1;
		//如果找的关键字,根据BST的性质,
		//则其排名为左子树的大小+1 
	else if (x>t[k].val)
			return t[t[k].l].sz+t[k].recy+atrank(t[k].r,x);
		//加上前面所有比它小的数,在右子树中找 
	   else return atrank(t[k].l,x);
	   	//如果在左子树中找的话就不用加了 
}


int rerank(int k,int x){//寻找排名为x的数值 
	if (k==0) return 0;
	if (x<=t[t[k].l].sz) return rerank(t[k].l,x);
		//如果x小于了左子树的大小,那解一定在左子树中 
	else if (x>t[t[k].l].sz+t[k].recy)
			return rerank(t[k].r,x-t[k].recy-t[t[k].l].sz);
		//如果x大于的左子树的大小+k的重复次数,
		//那就在右子树中找 
	   else return t[k].val;
	    	//否则就是找到解了(包含了重复数字中) 
}


void pred(int k,int x){//找前缀 
	if (k==0) return ;
	if (t[k].valx){
		ans=k;succ(t[k].l,x);
	}else succ(t[k].r,x);
}


int main(){
	int f,x;
	scanf("%d",&m);
	for (int i=1;i<=m;++i){
		scanf("%d%d",&f,&x);ans=0;
		if (f==1) insert(root,x);
		if (f==2) del(root,x);
		if (f==3) printf("%d\n",atrank(root,x));
		if (f==4) printf("%d\n",rerank(root,x));
		if (f==5) {pred(root,x);printf("%d\n",t[ans].val);}
		if (f==6) {succ(root,x);printf("%d\n",t[ans].val);}
	}
	return 0;
}


你可能感兴趣的:(【模板】Treap (模板题:洛谷P3369普通平衡树))