[Treap]普通平衡树(番外加强版)

33 篇文章 0 订阅
4 篇文章 0 订阅

题面

题目描述

您需要写一种数据结构,来维护一些数,其中需要提供以下操作:

  1. 插入 x \text{x} x
  2. 删除 x \text{x} x数(若有多个相同的数,因只删除一个)
  3. 查询 x \text{x} x数的排名(排名定义为比当前数小的数的个数 +1 \text{+1} +1 )
  4. 查询排名为 x \text{x} x的数
  5. x \text{x} x的前驱(前驱定义为小于 x \text{x} x,且最大的数)
  6. x \text{x} x的后继(后继定义为大于 x \text{x} x,且最小的数)

输入格式

第一行为 n \text{n} n,表示操作的个数,下面 n \text{n} n 行每行有两个数 opt \text{opt} opt x x x opt \text{opt} opt 表示操作的序号 ( 1 ≤ opt ≤ 6 ) ( 1 \leq \text{opt} \leq 6) (1opt6)

输出格式

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

样例数据

输入

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

输出

106465
84185
492737

数据范围

对于 100 % \text{100}\% 100% 的数据, 1 ≤ n ≤ 1 0 5 1\le n \le 10^5 1n105 ∣ x ∣ ≤ 1 0 7 |x| \le 10^7 x107

算法解析

概念

Treap

树堆 \text{树堆} 树堆,在数据结构中也称 Treap \text{Treap} Treap,是指有一个随机附加域满足堆的性质的 二叉搜索树 [ 1 ] \text{二叉搜索树}^{[1]} 二叉搜索树[1],其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为 O ( log ⁡ 2 n ) O(\log_{2}{n}) O(log2n)。相对于其他的平衡二叉搜索树, Treap \text{Treap} Treap的特点是 实现简单 \text{实现简单} 实现简单,且能基本 实现随机平衡 \text{实现随机平衡} 实现随机平衡的结构。
Treap \text{Treap} Treap是一棵 二叉排序树 [ 1 ] \text{二叉排序树}^{[1]} 二叉排序树[1],它的左子树和右子树分别是一个 Treap \text{Treap} Treap,和一般的二叉排序树不同的是, Treap \text{Treap} Treap纪录一个额外的数据,就是 优先级 \text{优先级} 优先级 Treap \text{Treap} Treap在以关键码构成二叉排序树的同时,还满足 堆 \text{堆} 的性质(在这里我们假设节点的优先级大于该节点的孩子的优先级)。

二叉搜索树/二叉排序树

二 叉 搜 索 树 / 二 叉 排 序 树 [ 1 ] {二叉搜索树/二叉排序树}^{[1]} /[1]
二 叉 查 找 树 二叉查找树 B i n a r y S e a r c h T r e e Binary Search Tree BinarySearchTree),(又: 二 叉 搜 索 树 二叉搜索树 二 叉 排 序 树 二叉排序树
它或者是一棵空树,或者是具有下列性质的二叉树:

  1. 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  2. 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  3. 它的左、右子树也分别为二叉排序树。

应用

操作1

针对本题,我们可以先对数据进行处理建模:
例如像这组数据
12 5 18 2 9 15 19 17 16
就可以造成如图的树
在这里插入图片描述
做到这样只需要利用二叉搜索树的性质(上文提到)
递归找到节点所应该在的位置

#include<bits/stdc++.h>
#define INF 0x3fffffff
using namespace std;
int K,N,M,op,q,root,last,ans=0;
struct Node{//定义节点
	int val,l,r,cnt,size,f;
	//val是数值,l、r是左右儿子,cnt是当前值出现的个数,
	//size是当前节点和字数的个数,f是一个随机数
}node[10000039];
void pushup(int p){
	node[p].size=node[node[p].l].size+node[node[p].r].size+node[p].cnt;
	//所有的节点数是左儿子的节点数加右儿子的节点数加当前节点的个数
}
int newnode(int x){//新建节点(k为节点的个数)
	node[++K].val=x,node[K].cnt=node[K].size=1;//数值为x,cnt是1,子树和当前的总节点数是1
	node[K].f=rand();//f是一个随机数
	return K;
}
void insert(int &p,int x){//插入操作(操作1)
	if(!p)p=newnode(x);//找到空的位置就新建一个节点
	else if(x==node[p].val){node[p].cnt++,node[p].size++;}
	//如果这个节点已经有过就cnt++,并同时size++
	else if(x<node[p].val){insert(node[p].l,x);if(node[node[p].l].f<node[p].f)zag(p);}
	//如果x比当前值小,就向左边走
	else if(x>node[p].val){insert(node[p].r,x);if(node[node[p].r].f<node[p].f)zig(p);}
	//如果x比当前值大,就向右边走
	pushup(p);
}

0 x 3 f f f f f f f = 1073741823 0x3fffffff=1073741823 0x3fffffff=1073741823
0 x f f f f f f f f = 4294967295 = ? 0xffffffff=4294967295=? 0xffffffff=4294967295=?
(这两句话是拿来干嘛的

操作2

zag和zig是右旋和左旋
在这里插入图片描述

void zag(int &p){
	int k=node[p].l;
	node[p].l=node[k].r,node[k].r=p,p=k;
	pushup(node[p].r),pushup(p);
}
void zig(int &p){
	int k=node[p].r;
	node[p].r=node[k].l,node[k].l=p,p=k;
	pushup(node[p].l),pushup(p);
}
void erase(int &p,int x){
	if(node[p].val==x){//如果当前点与x相同
		if(node[p].cnt>1)node[p].cnt--;//个数大于1就直接cnt--
		else if(!node[p].l)p=node[p].r;//如果没有左儿子就向右
		else if(!node[p].r)p=node[p].l;//如果没有右儿子就向左
		//都有的话就看谁的f小,就往哪边转
		else if(node[node[p].l].f<node[node[p].r].f)zag(p),erase(node[p].r,x);
		else if(node[node[p].l].f>node[node[p].r].f)zig(p),erase(node[p].l,x);
	}
	else if(node[p].val>x)erase(node[p].l,x);
	else if(node[p].val<x)erase(node[p].r,x);
	//同样向左或向右
	pushup(p);
}

操作3、4

int getrank(int p,int x){
	if(!p)return 0;
	else if(node[p].val>x)return getrank(node[p].l,x);
	else if(node[p].val<x)return getrank(node[p].r,x)+node[node[p].l].size+node[p].cnt;
	else if(node[p].val==x)return node[node[p].l].size+1;
}
int getval(int p,int x){
	if(x<=node[node[p].l].size+node[p].cnt&&x>node[node[p].l].size)return node[p].val;
	else if(x<=node[node[p].l].size)return getval(node[p].l,x);
	else return getval(node[p].r,x-node[node[p].l].size-node[p].cnt);
}//自己理解吧

操作5、6

int get_pre(int p,int x){
	if(!p)return -INF;
	else if(x>node[p].val)return comp(node[p].val,get_pre(node[p].r,x),1);
	else return get_pre(node[p].l,x);
}
int get_nex(int p,int x){
	if(!p)return INF;
	else if(x<node[p].val)return comp(node[p].val,get_nex(node[p].l,x),0);
	else return get_nex(node[p].r,x);
}

主程序

int main(){
	srand((unsigned int)time(0));
	newnode(-INF),newnode(INF);
	root=1,node[1].r=2;pushup(1);
	scanf("%d",&N);
	while(N--){
		scanf("%d%d",&op,&q);
		if(op==1){insert(root,q);}
		if(op==2){erase(root,q);}
		if(op==3){printf("%d\n",getrank(root,q)-1);}
		if(op==4){printf("%d\n",getval(root,q+1));}
		if(op==5){printf("%d\n",get_pre(root,q));}
		if(op==6){printf("%d\n",get_nex(root,q));}
	}
	return 0;
}

完整代码

不要直接复制

#include<bits/stdc++.h>
#define INF 0xffffffff
using namespace std;
int K,N,op,q,root;
struct Node{int val,l,r,cnt,size,f;}node[100039];
int comp(int a,int b,bool x){return x?(a>b?a:b):(a>b?b:a);}
int newnode(int x){node[++K].val=x,node[K].cnt=node[K].size=1;node[K].f=rand();return K;}
void pushup(int p){node[p].size=node[node[p].l].size+node[node[p].r].size+node[p].cnt;}
void zag(int &p){
	int k=node[p].l;
	node[p].l=node[k].r,node[k].r=p,p=k;
	pushup(node[p].r),pushup(p);
}
void zig(int &p){
	int k=node[p].r;
	node[p].r=node[k].l,node[k].l=p,p=k;
	pushup(node[p].l),pushup(p);
}
void insert(int &p,int x){
	if(!p)p=newnode(x);
	else if(x==node[p].val){node[p].cnt++,node[p].size++;}
	else if(x<node[p].val){insert(node[p].l,x);if(node[node[p].l].f<node[p].f)zag(p);}
	else if(x>node[p].val){insert(node[p].r,x);if(node[node[p].r].f<node[p].f)zig(p);}
	pushup(p);
}
void erase(int &p,int x){
	if(node[p].val==x){
		if(node[p].cnt>1)node[p].cnt--;
		else if(!node[p].l)p=node[p].r;
		else if(!node[p].r)p=node[p].l;
		else if(node[node[p].l].f<node[node[p].r].f)zag(p),erase(node[p].r,x);
		else if(node[node[p].l].f>node[node[p].r].f)zig(p),erase(node[p].l,x);
	}
	else if(node[p].val>x)erase(node[p].l,x);
	else if(node[p].val<x)erase(node[p].r,x);
	pushup(p);
}
int getrank(int p,int x){
	if(!p)return 0;
	else if(node[p].val>x)return getrank(node[p].l,x);
	else if(node[p].val<x)return getrank(node[p].r,x)+node[node[p].l].size+node[p].cnt;
	else if(node[p].val==x)return node[node[p].l].size+1;
}
int getval(int p,int x){
	if(x<=node[node[p].l].size+node[p].cnt&&x>node[node[p].l].size)return node[p].val;
	else if(x<=node[node[p].l].size)return getval(node[p].l,x);
	else return getval(node[p].r,x-node[node[p].l].size-node[p].cnt);
}
int get_pre(int p,int x){
	if(!p)return -INF;
	else if(x>node[p].val)return comp(node[p].val,get_pre(node[p].r,x),1);
	else return get_pre(node[p].l,x);
}
int get_nex(int p,int x){
	if(!p)return INF;
	else if(x<node[p].val)return comp(node[p].val,get_nex(node[p].l,x),0);
	else return get_nex(node[p].r,x);
}
int main(){
	srand((unsigned int)time(0));
	newnode(-INF),newnode(INF);
	root=1,node[1].r=2;pushup(1);
	scanf("%d",&N);
	while(N--){
		scanf("%d%d",&op,&q);
		if(op==1){insert(root,q);}
		if(op==2){erase(root,q);}
		if(op==3){printf("%d\n",getrank(root,q)-1);}
		if(op==4){printf("%d\n",getval(root,q+1));}
		if(op==5){printf("%d\n",get_pre(root,q));}
		if(op==6){printf("%d\n",get_nex(root,q));}
	}
	return 0;
}

番外加强版

输入格式
第一行两个正整数 n,m \text{n,m} n,m,表示初始数的个数和操作的个数。

第二行 n \text{n} n个整数 a 1 , a 2 , a 3 , … , a n a_{1},a_{2},a_{3},\ldots,a_{n} a1,a2,a3,,an ,表示初始的数。
接下来 m \text{m} m行,每行有两个整数 opt \text{opt} opt x x x 表示操作的序号 ( 1 ≤ opt ≤ 6 ) , x (1 \leq \text{opt} \leq 6),x 1opt6,x表示加密后的操作数。

我们记 last \text{last} last 表示上一次 3 , 4 , 5 , 6 3,4,5,6 3,4,5,6 操作的答案,则每次操作的 x x x 都要异或上 last \text{last} last才是真实的 x x x。初始 last \text{last} last 0 0 0

输出格式
输出一行一个整数,表示所有 3 , 4 , 5 , 6 3,4,5,6 3,4,5,6 操作的答案的异或和。
样例
输入

6 7
1 1 4 5 1 4
2 1
1 9
4 1
5 8
3 13
6 7
1 4

输出

6

解析
只要把INF该大点
node开大点
再改亿 一下主程序

#include<bits/stdc++.h>
#define INF 0x7fffffff
using namespace std;
int K,N,M,op,q,root,last,p,ans;
struct Node{int val,l,r,cnt,size,f;}node[3000039];
template<typename item>
inline void read(register item &x){
	x=0;register char c=getchar();register bool f=0;
	while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
	if(f)x=-x;
}
int comp(int a,int b,bool x){return x?(a>b?a:b):(a>b?b:a);}
int newnode(int x){node[++K].val=x,node[K].cnt=node[K].size=1;node[K].f=rand();return K;}
void pushup(int p){node[p].size=node[node[p].l].size+node[node[p].r].size+node[p].cnt;}
void zag(int &p){
	int k=node[p].l;
	node[p].l=node[k].r,node[k].r=p,p=k;
	pushup(node[p].r),pushup(p);
}
void zig(int &p){
	int k=node[p].r;
	node[p].r=node[k].l,node[k].l=p,p=k;
	pushup(node[p].l),pushup(p);
}
void insert(int &p,int x){
	if(!p)p=newnode(x);
	else if(x==node[p].val){node[p].cnt++,node[p].size++;}
	else if(x<node[p].val){insert(node[p].l,x);if(node[node[p].l].f<node[p].f)zag(p);}
	else if(x>node[p].val){insert(node[p].r,x);if(node[node[p].r].f<node[p].f)zig(p);}
	pushup(p);
}
void erase(int &p,int x){
	if(node[p].val==x){
		if(node[p].cnt>1)node[p].cnt--;
		else if(!node[p].l)p=node[p].r;
		else if(!node[p].r)p=node[p].l;
		else if(node[node[p].l].f<node[node[p].r].f)zag(p),erase(node[p].r,x);
		else if(node[node[p].l].f>node[node[p].r].f)zig(p),erase(node[p].l,x);
	}
	else if(node[p].val>x)erase(node[p].l,x);
	else if(node[p].val<x)erase(node[p].r,x);
	pushup(p);
}
int getrank(int p,int x){
	if(!p)return 1;
	else if(node[p].val>x)return getrank(node[p].l,x);
	else if(node[p].val<x)return getrank(node[p].r,x)+node[node[p].l].size+node[p].cnt;
	else if(node[p].val==x)return node[node[p].l].size+1;
	return 1;
}
int getval(int p,int x){
	if(x<=node[node[p].l].size+node[p].cnt&&x>node[node[p].l].size)return node[p].val;
	else if(x<=node[node[p].l].size)return getval(node[p].l,x);
	else return getval(node[p].r,x-node[node[p].l].size-node[p].cnt);
}
int getpre(int p,int x){
	if(!p)return -INF;
	else if(x>node[p].val)return comp(node[p].val,getpre(node[p].r,x),1);
	else return getpre(node[p].l,x);
}
int getnex(int p,int x){
	if(!p)return INF;
	else if(x<node[p].val)return comp(node[p].val,getnex(node[p].l,x),0);
	else return getnex(node[p].r,x);
}
int main(){
	//freopen("1.in","r",stdin);
	srand((unsigned int)time(0));
	newnode(-INF),newnode(INF);
	root=1,node[1].r=2;pushup(1);
	read(N);read(M);
	while(N--){read(q);insert(root,q);}
	while(M--){
		read(op),read(q);q^=last;
		if(op==1){insert(root,q);}
		if(op==2){erase(root,q);}
		if(op==3){last=getrank(root,q)-1;}
		if(op==4){last=getval(root,q+1);}
		if(op==5){last=getpre(root,q);}
		if(op==6){last=getnex(root,q);}
		if(op>=3){ans^=last;}
	}
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值