关于非旋转Treap
刚刚跟着EM-LGH大佬学了非旋转Treap
非常庆幸不用再写万恶的rotate了(来自高级数据结构的恶意)
来记一下
Treap
概念
简单来说,\(Tree_{二叉搜索树} * Heap_堆 = Treap_{平衡树}\)
这显然不是袁隆平爷爷干的
显然这两样东西有各自的排列顺序——左小右大以及根小(大)儿子大(小)
对于寻找答案来讲,二叉搜索树更加方便
— 那么堆用来干嘛呢
很简单,用来达到期望平衡
— 怎么实现呢
通过另一个关键字
— 为什么是“期望”平衡呢
因为是通过随机的关键字啊!
操作
上面说过了,二叉搜索树管答案,堆管时间
李云龙:“你管生活,我管军事”
如何让随机的关键字满足堆的性质,同时节点的值满足二叉搜索树的性质呢
旋转
然而这个玩意十分难写且难理解。。。
所以就出现了……
非旋转Treap
它与旋转的Treap很相似
但是它是基于分裂和合并两个基本操作而不是旋转
-define表+struct,请对照此表理解代码-
#define lson t[x].ls
#define rson t[x].rs
#define si t[x].size
#define ra t[x].ran
#define lss t[t[x].ls].size
#define rss t[t[x].rs].size
#define va t[x].val
//-------------------------
struct node
{
int val, size, ls, rs, ran;
}t[100001];
新建节点
正常的初始化
inline void newnode(int &x, int val)
{
++tot;
t[tot].size=1;
t[tot].val=val;
t[tot].ran=rand();
t[tot].ls=t[tot].rs=0;
x=tot;
}
分裂
指定一个val,将值∈[0, val]的节点与值∈(val, +∞)的节点分成两棵树
实现过程和寻找后继的过程很像
void split(int x, int &l, int &r, int val)
{
if(!x)
{
l = r = 0;
return;
}
if(va <= val) l = x, split(t[x].rs, t[l].rs, r, val);//当前值比val小或等于val,则将它与它的左子树全部划分到第一棵树,继续寻找它的右子树
else r = x, split(t[x].ls, l, t[r].ls, val);//反之,则将它与它的右子树划分到第二棵树,寻找它的左子树
pushup(x);//不要忘记更新size
}
合并
分裂的反过程
要求合并的A树与B树中\(A_{max} < B_{min}\)
void merge(int &x, int a, int b)
{
if(!a||!b)
{
x = a + b;
return;
}
if(t[a].ran < t[b].ran) x = a, merge(t[x].rs, t[a].rs, b);//随机值在这里用,用来在合并时维护堆的性质
else x = b, merge(t[x].ls, a, t[b].ls);
pushup(x);//更新!
}
插入
基于分裂和合并
在\(val - 1\)处分裂->合并节点Z与树A->合并树A与树B
void insert(int val)
{
int x = 0, y = 0, z = 0;
newnode(z, val);
split(root, x, y, val - 1);
merge(x, x, z);
merge(root, x, y);
}
删除
和插入很像
将大树在\(val - 1\)处分裂成AB->将树B在\(val\)处分裂成BC->合并树A与树C
void del(int val)
{
int x = 0, y = 0, z = 0;
split(root, x, y, val);
split(x, x, z, val - 1);
merge(z, t[z].ls, t[z].rs);//这里是只删除一个的操作,全部删除请忽略本行和下一行
merge(x, x, z);
merge(root, x, y);
}
询问排名
和插入很像
在\(val-1\)处分裂->输出A的size
void ask_rank(int v)
{
int x = 0, y = 0;
split(root, x, y, v - 1);
cout << si + 1;
merge(root, x, y);
}
询问第k小
相当于反着问排名
void ask_num(int x, int kth)
{
while(lss + 1 != kth)
{
if(lss >= kth) x = lson;
else kth -= (lss + 1), x = rson;
}
cout << va;
}
前驱
在\(v-1\)处分裂->询问A中最大(第size小)->合并
void ask_fr(int v)
{
int x = 0, y = 0;
split(root, x, y, v - 1);
ask_num(x, si);
merge(root, x, y);
}
后继
与前驱相反
在\(v\)处分裂->询问B中第一小->合并
void ask_ba(int v)
{
int x = 0, y = 0;
split(root, x, y, v);
ask_num(y, 1);
merge(root, x, y);
}
时间复杂度
由于它是期望平衡的,所以它的所有操作都在\(O(logN)\)左右。
总代码(以Luogu P3369为例)
#include <bits/stdc++.h>
#define lson t[x].ls
#define rson t[x].rs
#define si t[x].size
#define ra t[x].ran
#define lss t[t[x].ls].size
#define rss t[t[x].rs].size
#define va t[x].val
using namespace std;
int root;
namespace treap
{
int tot;
struct node
{
int val, size, ls, rs, ran;
}t[100001];
inline void newnode(int &x, int val)
{
++tot;
t[tot].size=1;
t[tot].val=val;
t[tot].ran=rand();
t[tot].ls=t[tot].rs=0;
x=tot;
}
inline void pushup(int x)
{
si = lss + rss + 1;
}
void split(int x, int &l, int &r, int val)
{
if(!x)
{
l = r = 0;
return;
}
if(va <= val) l = x, split(t[x].rs, t[l].rs, r, val);
else r = x, split(t[x].ls, l, t[r].ls, val);
pushup(x);
}
void merge(int &x, int a, int b)
{
if(!a||!b)
{
x = a + b;
return;
}
if(t[a].ran < t[b].ran) x = a, merge(t[x].rs, t[a].rs, b);
else x = b, merge(t[x].ls, a, t[b].ls);
pushup(x);
}
void insert(int val)
{
int x = 0, y = 0, z = 0;
newnode(z, val);
split(root, x, y, val - 1);
merge(x, x, z);
merge(root, x, y);
}
void del(int val)
{
int x = 0, y = 0, z = 0;
split(root, x, y, val);
split(x, x, z, val - 1);
merge(z, t[z].ls, t[z].rs);
merge(x, x, z);
merge(root, x, y);
}
void ask_rank(int v)
{
int x = 0, y = 0;
split(root, x, y, v - 1);
cout << si + 1;
merge(root, x, y);
}
void ask_num(int x, int kth)
{
while(lss + 1 != kth)
{
if(lss >= kth) x = lson;
else kth -= (lss + 1), x = rson;
}
cout << va;
}
void ask_fr(int v)
{
int x = 0, y = 0;
split(root, x, y, v - 1);
ask_num(x, si);
merge(root, x, y);
}
void ask_ba(int v)
{
int x = 0, y = 0;
split(root, x, y, v);
ask_num(y, 1);
merge(root, x, y);
}
};
using namespace treap;
int main()
{
int n;
cin >> n;
srand(2005);
while(n--)
{
int x, y;
cin >> x >> y;
if(x == 1) insert(y);
else if(x == 2) del(y);
else if(x == 3) ask_rank(y), cout << endl;
else if(x == 4) ask_num(root, y), cout << endl;
else if(x == 5) ask_fr(y), cout << endl;
else if(x == 6) ask_ba(y), cout << endl;
}
return 0;
}
完结,撒花!!!!!!!★,°:.☆( ̄▽ ̄)/$:.°★ 。
关于非旋转Treap的更多相关文章
- [bzoj3173]最长上升子序列_非旋转Treap
最长上升子序列 bzoj-3173 题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度. 注释:1<=n<=10,000,开始序列为 ...
- 关于非旋转treap的学习
非旋转treap的操作基于split和merge操作,其余操作和普通平衡树一样,复杂度保证方式与旋转treap差不多,都是基于一个随机的参数,这样构出的树树高为\(logn\) split 作用:将原 ...
- [Codeforces702F]T-Shirts——非旋转treap+贪心
题目链接: Codeforces702F 题目大意:有$n$种T恤,每种有一个价格$c_{i}$和品质$q_{i}$且每种数量无限.现在有$m$个人,第$i$个人有$v_{i}$元,每人每次会买他能买 ...
- BZOJ5063旅游——非旋转treap
题目描述 小奇成功打开了大科学家的电脑. 大科学家打算前往n处景点旅游,他用一个序列来维护它们之间的顺序.初 始时,序列为1,2,...,n. 接着,大科学家进行m次操作来打乱顺序.每次操作有6步: ...
- BZOJ3223文艺平衡树——非旋转treap
此为平衡树系列第二道:文艺平衡树您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作: 翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...
- BZOJ3224普通平衡树——非旋转treap
题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数, ...
- [NOIP]2017列队——旋转treap/非旋转treap
Sylvia 是一个热爱学习的女孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为m. 为了便 ...
- BZOJ3729Gty的游戏——阶梯博弈+巴什博弈+非旋转treap(平衡树动态维护dfs序)
题目描述 某一天gty在与他的妹子玩游戏.妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问将某个节点的子树中的石子移动到这个节点先手是否有必胜策略.gt ...
- BZOJ1552[Cerc2007]robotic sort&BZOJ3506[Cqoi2014]排序机械臂——非旋转treap
题目描述 输入 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000. 第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. 输出 输出共一行,N个用空格隔开 ...
- BZOJ1251序列终结者——非旋转treap
题目描述 网上有许多题,就是给定一个序列,要你支持几种操作:A.B.C.D.一看另一道题,又是一个序列 要支持几种操作:D.C.B.A.尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技 ...
随机推荐
- [C语言学习笔记一]基本构架和变量
基本构架 所有的C程序都有一个 main 函数.其后包含在大括号中的是 main 函数的内容. main函数是程序的入口,程序运行后,先进入 main 函数,然后一次执行 main 函数体中的语句. ...
- 暑假第五周总结(学习HBASE相关知识)
本周主要对HBASE的相关知识进行了学习,主要是通过视频的讲解了解到了HBASE的存储机制,HBASE的机制与普通的关系型数据库完全不同,HBASE以列进行存储,其主要执行的就是增删查操作,其更改主要 ...
- python中Threadlocal变量
在多线程环境下,每个线程都有自己的数据.一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程,而全局变量的修改必须加锁. 不加锁就会出现变量会被修改的问题,进而 ...
- cookie理解与实践【实现简单登录以及自动登录功能】
cookie理解 Cookie是由W3C组织提出,最早由netscape社区发展的一种机制 http是无状态协议.当某次连接中数据提交完,连接会关闭,再次访问时,浏览器与服务器需要重新建立新的连接: ...
- 导弹拦截(dp复习)
题目描述:求最长下降子序列的长度 in: 71 7 3 5 9 4 8 out: 4 解释:1 3 4 8/1 3 5 8 长度为四 这让我想到了hdu上导弹拦截那道题,是求最长上升子序列长度 其转移 ...
- CCF_201312-5_I’m stuck!
一次bfs从起点开始找到起点能到达的点,一次bfs从终点开始找到能到终点的点,最后输出答案即可. 刚开始写的时候,考虑找起点能到达的点的时候,用了dfs,提交只有20分,仔细想了一下,会存在无限循环的 ...
- Educational Codeforces Round 76 (Rated for Div. 2) E. The Contest
Educational Codeforces Round 76 (Rated for Div. 2) E. The Contest(dp+线段树) 题目链接 题意: 给定3个人互不相同的多个数字,可以 ...
- 不同宿主的iterator不能进行比较
int main() { string str1, str2; auto it1 = str1.begin(), it2 = str2.begin(); it1 == it2; ; }
- 前端jQuery日历控件报错 $("#datepicker").datepicker is not a function
使用日历控件时,前端产生错误: $("#datepicker").datepicker is not a function 问题原因 前端在同一个页面,jQuery引入了两次. 解 ...
- golang中使用Shutdown特性对http服务进行优雅退出使用总结
golang 程序启动一个 http 服务时,若服务被意外终止或中断,会让现有请求连接突然中断,未处理完成的任务也会出现不可预知的错误,这样即会造成服务硬终止:为了解决硬终止问题我们希望服务中断或退出 ...