题面
题目描述
您需要写一种数据结构,来维护一些数,其中需要提供以下操作:
- 插入 x \text{x} x数
- 删除 x \text{x} x数(若有多个相同的数,因只删除一个)
- 查询 x \text{x} x数的排名(排名定义为比当前数小的数的个数 +1 \text{+1} +1 )
- 查询排名为 x \text{x} x的数
- 求 x \text{x} x的前驱(前驱定义为小于 x \text{x} x,且最大的数)
- 求 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) (1≤opt≤6)
输出格式
对于操作 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 1≤n≤105, ∣ x ∣ ≤ 1 0 7 |x| \le 10^7 ∣x∣≤107
算法解析
概念
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
针对本题,我们可以先对数据进行处理建模:
例如像这组数据
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;
}