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

UESTC395 Dynamic Query System 【简单平衡树(数组Treap)】_第1张图片

UESTC395 Dynamic Query System 【简单平衡树(数组Treap)】_第2张图片

【题目大意】

题目包含多组数据

每组数据读入一个正整数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的做法是旋转,下面是配图和代码:

UESTC395 Dynamic Query System 【简单平衡树(数组Treap)】_第3张图片


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;
}


【总结】

平衡树本质——堆+二叉搜索树

你可能感兴趣的:(树,平衡树,Treap,实用数据结构)