您的位置:首页 > 产品设计 > UI/UE

UESTC395 Dynamic Query System 【简单平衡树(数组Treap)】

2017-04-21 20:50 387 查看




【题目大意】

题目包含多组数据

每组数据读入一个正整数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<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<cmath>
4000

#include<cctype>
#include<iomanip>
#include<cstring>
#include<string>
#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 : (x<v ? 0 : 1);
}
}tree
;
int root;
int cnt;

void Clear(){
cnt=0;
root=0;
memset(tree,0,sizeof(tree));
}

int Build(int x){
int newnode;
newnode=++cnt;
tree[newnode].p=tree[newnode].s=1;
tree[newnode].r=rand();
tree[newnode].v=x;
return newnode;
}

void Maintain(int o){
tree[o].s=tree[o].p+tree[son(o,0)].s+tree[son(o,1)].s;
}

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(tmp);
o=tmp;
}

void Insert(int &o,int x){
if (!o) o=Build(x);
else{
int d=tree[o].cmp(x);
if (d==-1){
tree[o].p++;
tree[o].s++;
return;
}
Insert(son(o,d),x);
tree[o].s++;
if (tree[son(o,d)].r>tree[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;
}


【总结】
平衡树本质——堆+二叉搜索树
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: