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; }
【总结】
平衡树本质——堆+二叉搜索树
相关文章推荐
- UESTC 395 Dynamic Query System --Treap
- System.Dynamic.ExpandoObject 类型的简单使用
- Dynamic Query System 基本SBT
- Dynamic Linq Query 简单API
- 浅谈C#中的数组类System.Array 操作
- 自定义通用System.Web.UI.IHierarchicalDataSource简单实现
- 简单的System.IFormattable实现示例:
- 一维数组的简单封装 carray模板。
- 利用textbox接收两个数,列出一个数组,并做简单的运算
- 简单的2维数组操作
- System.IndexOutOfRangeException: 索引超出了数组界限
- System.Security.SecurityException: 不允许所请求的注册表访问权 一个简单的解决办法
- 使用System.DirectoryServices.Protocols实现对AD的简单操作
- 一个简单的基于System.Web.Mail的ASP.Net邮件发送程序
- 一个简单的oracle函数返回数组的例子
- 使用System.DirectoryServices.Protocols实现对AD的简单操作
- 发现system.collection 命名空间下面的类大部分是基于数组来存储
- C的一些简单习题(13)--数组a中n个整数按相反顺序存放
- 用C#实现简单的控件数组
- 常见内部排序算法 简单数组实现与分析(快速(偶原创partition函数,望众高手指正)、归并、希尔、插入、选择、冒泡)