刚刚跟着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的更多相关文章

  1. [bzoj3173]最长上升子序列_非旋转Treap

    最长上升子序列 bzoj-3173 题目大意:有1-n,n个数,第i次操作是将i加入到原有序列中制定的位置,后查询当前序列中最长上升子序列长度. 注释:1<=n<=10,000,开始序列为 ...

  2. 关于非旋转treap的学习

    非旋转treap的操作基于split和merge操作,其余操作和普通平衡树一样,复杂度保证方式与旋转treap差不多,都是基于一个随机的参数,这样构出的树树高为\(logn\) split 作用:将原 ...

  3. [Codeforces702F]T-Shirts——非旋转treap+贪心

    题目链接: Codeforces702F 题目大意:有$n$种T恤,每种有一个价格$c_{i}$和品质$q_{i}$且每种数量无限.现在有$m$个人,第$i$个人有$v_{i}$元,每人每次会买他能买 ...

  4. BZOJ5063旅游——非旋转treap

    题目描述 小奇成功打开了大科学家的电脑. 大科学家打算前往n处景点旅游,他用一个序列来维护它们之间的顺序.初 始时,序列为1,2,...,n. 接着,大科学家进行m次操作来打乱顺序.每次操作有6步: ...

  5. BZOJ3223文艺平衡树——非旋转treap

    此为平衡树系列第二道:文艺平衡树您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作: 翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1 ...

  6. BZOJ3224普通平衡树——非旋转treap

    题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数, ...

  7. [NOIP]2017列队——旋转treap/非旋转treap

    Sylvia 是一个热爱学习的女孩子.  前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia所在的方阵中有n × m名学生,方阵的行数为 n,列数为m.  为了便 ...

  8. BZOJ3729Gty的游戏——阶梯博弈+巴什博弈+非旋转treap(平衡树动态维护dfs序)

    题目描述 某一天gty在与他的妹子玩游戏.妹子提出一个游戏,给定一棵有根树,每个节点有一些石子,每次可以将不多于L的石子移动到父节点,询问将某个节点的子树中的石子移动到这个节点先手是否有必胜策略.gt ...

  9. BZOJ1552[Cerc2007]robotic sort&BZOJ3506[Cqoi2014]排序机械臂——非旋转treap

    题目描述 输入 输入共两行,第一行为一个整数N,N表示物品的个数,1<=N<=100000. 第二行为N个用空格隔开的正整数,表示N个物品最初排列的编号. 输出 输出共一行,N个用空格隔开 ...

  10. BZOJ1251序列终结者——非旋转treap

    题目描述 网上有许多题,就是给定一个序列,要你支持几种操作:A.B.C.D.一看另一道题,又是一个序列 要支持几种操作:D.C.B.A.尤其是我们这里的某人,出模拟试题,居然还出了一道这样的,真是没技 ...

随机推荐

  1. 字符串(String)的创建,以及字符串的属性及方法

    1.String对象的创建 方法1: var txt = new String("i am String"); console.log(txt); // 结果为:i am Stri ...

  2. qt creator源码全方面分析(0)

    本人主攻C++和Qt. 上两天刚研究完Qt install framework(IFW)应用程序安装框架. google没发现有正儿八经的官方文档的翻译,我就进行了翻译哈!! 系列文章具体见:http ...

  3. 程序的健壮性Robustness

    所谓的程序健壮性是指处理异常的能力,在异常中能够独立处理异常,并且把正确的答案输出. 例如: 有一个程序能够下载一个文件到指定的路径,但是这个路径是不存在的,因此程序必须要处理这个情况. 例1:下面的 ...

  4. SpringBoot+MyBatis项目Dao层最简单写法

    前言 DAO(Data Access Object) 是数据访问层,说白了就是跟数据库打交道的,而数据库都有哪几种操作呢?没错,就是增删改查.这就意味着Dao层要提供增删改查操作. 不知道大家是怎么写 ...

  5. 下载STRING数据库检索互作关系结果为空,但是在STRING网站却能检索出互作关系,为什么呢???关键词用的是蛋白ID(ENSP开头)

    首先介绍下两种方法: 一.本地分析 1.在STRING数据库下载人的互作文件,如下图,第一个文件 https://string-db.org/cgi/download.pl?sessionId=HGr ...

  6. C语言寒假大作战03

    这个作业属于哪个课程 软件4班 这个作业要求在哪里 C语言寒假大作战03 这个作业的目标 增加菜单程序各年级题目操作函数 参考文献 随机数rand 2.2.2 设计思路和遇到的问题 这次作业写好没多久 ...

  7. 遇到的一些在ie下的兼容问题和解决方案(ie10+)

    1,ie 10下实现水平垂直居中,不固定高度的话,正常的top:50%,left:50%,transform(translate(-50%,-50%)) 是不能实现的,ie下top:50%会失去效果. ...

  8. LinearLayout里面的空间居中对齐

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  9. Pycharm2019.1.1永久破解

    声明:不做商业用途,电脑系统win10专业版,亲测有效. 1,下载新版破解补丁+安装包 网盘链接 提取码:hn0u 下载补丁文件 jetbrains-agent.jar 并将它放置到 pycharm安 ...

  10. [CentOS7]Yum 使用代理

    公司服务器只允许YUM出公网 # vi /etc/yum.conf # 添加下面一行 proxy=http://10.1.1.1:55555 # proxy ip:端口 END