【题目大意】
题目包含多组数据
每组数据读入一个正整数n表示操作数量,接下来n行,每行一个操作
操作分为八种:
1)I X 表示将X插入序列
2)R X 表示将X从序列中删除,注意,由于X可能被插入了多次,所以只要随便删除一个即可
3)S 表示询问该序列的元素个数(重复的也算)
4)L X 表示询问序列中严格比X小的数有多少个
5)W K 表示询问第K大的数是什么(从1开始标号),如果K<=0||K>=序列总数,则输出-1
6)C X 表示询问X 这个数重复出现了几次
7)MI 表示询问整个序列的最小值
8)MA 表示询问整个序列的最大值
对于询问操作输出对应的值
【解题思路】
本题就是使用Treap的模板题,但是需要注意的是重复的节点。
说到这里,顺带着提一提Treap
笔者一开始尝试过指针版,但是由于特判太多,代码冗杂不方便查错,最后还是决定使用静态数组实现。
Treap 又名树堆(Tree+Heap,其名字的来源)是一种较为基础的平衡树
其本质是二叉搜索树,但是它满足堆的性质,对于一个点o,其左子树的所有点的值都比o的值小,同理,其右子树的所有点的值都比o大
我们都知道,平衡树的核心,就在于它如何保证树高限制在log级别内。
Treap的做法是旋转,下面是配图和代码:
void Rotate(int &o,int d){//d==0 left d==1 right
int tmp=son(o,d^1);
son(o,d^1)=son(tmp,d);
son(tmp,d)=o;
Maintain(o);//Maintain函数重新维护子树大小
Maintain(tmp);//必须先维护o,因为旋转之后o是tmp的子节点
o=tmp;
}
掌握了如何保证平衡之后,平衡树就相当于转化为堆和二分的操作了
【代码】
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
//#define LOCAL
using namespace std;
const int N=1000111;
struct TreapType{
#define son(o,d) tree[o].ch[d]
struct Node{
int ch[2];//左右儿子
int s;//子树大小
int r;//优先级,由随机确定,调整堆时需要
int v;//该节点的值
int p;//该节点重复出现的次数
int cmp(int x) const {
return x==v ? -1 : (xtree[o].r) Rotate(o,d^1);
}
Maintain(o);
}
void Remove(int &o,int x){
if (!o) return;
int d=tree[o].cmp(x);
if (d==-1){
if (tree[o].p>1){
tree[o].p--;
tree[o].s--;
return;
}
if (!son(o,0)) o=son(o,1);
else if (!son(o,1)) o=son(o,0);
else{
int tmp=tree[son(o,0)].r > tree[son(o,1)].r ? 1 : 0;
Rotate(o,tmp);
Remove(son(o,tmp),x);
}
}
else Remove(son(o,d),x);
Maintain(o);
}
int Count(int o,int x){//L操作
if (!o) return 0;
int d=tree[o].cmp(x);
if (d==-1) return tree[son(o,0)].s;
else if (!d) return Count(son(o,0),x);
else return tree[son(o,0)].s+tree[o].p+Count(son(o,1),x);
}
int Find(int o,int x){//W操作
if (!o) return 0;
int ret=tree[son(o,0)].s;
if (x<=ret) return Find(son(o,0),x);
else if (x>ret&&x<=ret+tree[o].p) return tree[o].v;
else return Find(son(o,1),x-ret-tree[o].p);
}
int Repeat(int o,int x){//C操作
if (!o) return 0;
int d=tree[o].cmp(x);
if (d==-1) return tree[o].p;
else if (!d) return Repeat(son(o,0),x);
else return Repeat(son(o,1),x);
}
int Getm(int o,int d){//d==0 min d==1 max
if (!son(o,d)) return tree[o].v;
return Getm(son(o,d),d);
}
}Treap;
int T,n;
int main(){
#ifdef LOCAL
freopen("UESTC395.in","r",stdin);
#endif
scanf("%d",&T);
while (T--){
Treap.Clear();
scanf("%d",&n);
while (n--){
char c[2];
scanf("%s",&c[0]);
int x;
switch (c[0]){
case 'I':scanf("%d",&x);Treap.Insert(Treap.root,x);break;
case 'R':scanf("%d",&x);Treap.Remove(Treap.root,x);break;
case 'S':printf("%d\n",Treap.tree[Treap.root].s);break;
case 'L':scanf("%d",&x);printf("%d\n",Treap.Count(Treap.root,x));break;
case 'W':scanf("%d",&x);printf("%d\n",(x<=0||x>Treap.tree[Treap.root].s) ? -1 : Treap.Find(Treap.root,x));break;
case 'C':scanf("%d",&x);printf("%d\n",Treap.Repeat(Treap.root,x));break;
case 'M':{
if (Treap.tree[Treap.root].s){
if (c[1]=='I') printf("%d\n",Treap.Getm(Treap.root,0));
else if (c[1]=='A') printf("%d\n",Treap.Getm(Treap.root,1));
}
else printf("-1\n");
break;
}
}
}
}
return 0;
}
平衡树本质——堆+二叉搜索树