debug3个月把洛谷刷屏,终于过了。。。。(一开始写了两百行,后来看了大孙代码才搞点小技巧改短了些)
treap,树堆,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为O(logn)。相对于其他的平衡二叉搜索树,Treap的特点是实现简单,且能基本实现随机平衡的结构。我们可以看到,如果一个二叉排序树节点插入的顺序是随机的,这样我们得到的二叉排序树大多数情况下是平衡的,即使存在一些极端情况,但是这种情况发生的概率很小,所以我们可以这样建立一颗二叉排序树,而不必要像AVL那样旋转,可以证明随机顺序建立的二叉排序树在期望高度是O(logn),但是某些时候我们并不能得知所有的带插入节点,打乱以后再插入。所以我们需要一种规则来实现这种想法,并且不必要所有节点。也就是说节点是顺序输入的,我们实现这一点可以用Treap。Treap=Tree+Heap。
Treap是一棵二叉排序树,它的左子树和右子树分别是一个Treap,和一般的二叉排序树不同的是,Treap纪录一个额外的数据,就是优先级。Treap在以关键码构成二叉排序树的同时,还满足堆的性质(在这里我们假设节点的优先级大于该节点的孩子的优先级)。但是这里要注意的是Treap和二叉堆有一点不同,就是二叉堆必须是完全二叉树,而Treap可以并不一定是。
以上来自百度百科。
简而言之,treap中序遍历就是原数组排好序后的排列,其中随机出来的优先级满足堆的性质。
与Splay不同之处是Splay的中序遍历就是原数组的排列顺序,而且与Splay的旋转操作等不同。treap比Splay好写(然而。。。)
新手很容易忽略的细节问题是一个数可能会添加多次,需要存一下每个数有多少个,每次查询和修改不要忘了它。
//Serene
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
const int maxn=100000+10;
int n,tot=0,ans1,ans2,root;
int aa,ff;char cc;
int read() {
aa=0;ff=1;cc=getchar();
while((cc<'0'||cc>'9')&&cc!='-') cc=getchar();
if(cc=='-') ff=-1,cc=getchar();
while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();
return aa*ff;
}
struct Node{
int num,rnd,son[2],sum,x;
}node[maxn];
void rotate(int &pos,int p) {
int s=node[pos].son[p];
node[s].sum=node[pos].sum;
node[pos].son[p]=node[s].son[!p];
node[s].son[!p]=pos;
node[pos].sum=node[pos].x+node[node[pos].son[0]].sum+node[node[pos].son[1]].sum;
pos=s;
}
void add(int &pos,int x) {
if(!pos) {
pos=++tot;node[pos].num=x;node[pos].rnd=rand();
node[pos].sum=node[pos].x=1;
return ;
}
node[pos].sum++;
if(node[pos].num==x) {
node[pos].x++; return;
}
int p=x>node[pos].num;
add(node[pos].son[p],x);
if(node[node[pos].son[p]].rnd<node[pos].rnd) rotate(pos,p);
}
void del(int &pos,int x) {
if(!pos) return;
if(node[pos].num==x) {
if(node[pos].x>1) {
node[pos].x--;node[pos].sum--;return;
}
if(node[pos].son[0]*node[pos].son[1]==0) {
pos=node[pos].son[0]+node[pos].son[1]; return;
}
int p= node[node[pos].son[1]].rnd<node[node[pos].son[0]].rnd;
rotate(pos,p);
node[pos].sum--;
del(node[pos].son[!p],x);
}
else {
node[pos].sum--;
if(node[pos].num>x) del(node[pos].son[0],x);
else del(node[pos].son[1],x);
}
}
int qrank(int pos,int x) {
if(node[pos].num==x) return node[node[pos].son[0]].sum+1;
if(node[pos].num>x) return qrank(node[pos].son[0],x);
return node[node[pos].son[0]].sum+node[pos].x+qrank(node[pos].son[1],x);
}
int qnum(int pos,int x) {
if(x>node[node[pos].son[0]].sum&&x<=node[node[pos].son[0]].sum+node[pos].x) return node[pos].num;
if(x<=node[node[pos].son[0]].sum) return qnum(node[pos].son[0],x);
return qnum(node[pos].son[1],x-node[pos].x-node[node[pos].son[0]].sum);
}
void q1(int pos,int x) {
if(!pos) return;
if(x>node[pos].num) {
ans1=node[pos].num;
q1(node[pos].son[1],x);
}
else q1(node[pos].son[0],x);
}
void q2(int pos,int x) {
if(!pos) return;
if(x<node[pos].num) {
ans2=node[pos].num;
q2(node[pos].son[0],x);
}
else q2(node[pos].son[1],x);
}
int main() {
srand((unsigned)time(NULL));
n=read();
int opt,x,y;
for(int i=1;i<=n;++i) {
opt=read();x=read();
if(opt==1) add(root,x);
else if(opt==2) del(root,x);
else if(opt==3) printf("%d\n",qrank(root,x));
else if(opt==4) printf("%d\n",qnum(root,x));
else if(opt==5) q1(root,x),printf("%d\n",ans1);
else q2(root,x),printf("%d\n",ans2);
}
return 0;
}