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



【题目大意】

题目包含多组数据

每组数据读入一个正整数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>
#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[N];
	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;
}


【总结】

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
互联网络程序设计是指在互联网上进行程序开发和设计的过程。UESTC则是我国的一所著名高校——电子科技大学。 互联网络程序设计 uestc包含了两个主要的方面:互联网络和程序设计。互联网络是指将多个计算机网络通过通信链路互相连接起来,实现信息共享和资源共享的网络系统。程序设计是指根据需求和目标,通过编写代码和设计算法,实现计算机程序的过程。 互联网络程序设计 uestc的学习内容主要包括以下几个方面: 1. 网络知识:学习互联网络的基本概念、原理和协议,如TCP/IP协议、HTTP协议等。掌握网络编程的基本技术,能够编写网络应用程序。 2. 据通信:学习据通信的基本原理和技术,包括据传输的方式、据压缩和加密等。了解网络安全和据保护的基本知识。 3. 程序设计:学习编程语言和开发工具,如Java、C++和Python等。掌握常用的编程技巧和方法,能够设计和实现复杂的网络应用程序。 4. Web开发:学习Web开发的基本知识和技术,包括HTML、CSS、JavaScript等。能够设计和实现交互式的Web应用程序。 5. 据库技术:学习据库的基本原理和技术,如SQL语言和据库管理系统。能够设计和管理据库,实现据的存储和检索。 通过学习互联网络程序设计 uestc,可以掌握互联网应用开发的基本技能,具备设计和实现网络应用程序的能力。这对于目前互联网行业的人才需求来说是非常重要的,也为学生提供了广阔的就业和创业机会。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值