今天我们来学习一种新的数据结构:无旋treap。它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化。

无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我们还是用rand()来实现平衡

而无旋treap与treap不同的地方,也是其核心,就是它不旋转用两个新的核心函数:merge函数(合并两棵子树)和split函数(分裂出某棵树的前k个节点,并且作为一棵树返回)

首先看merge函数,它是一个递归实现的过程,先看代码:

 Treap *merge(Treap *a,Treap *b)
{
if(a==null)return b;
if(b==null)return a;
pushdown(a);pushdown(b);
if(a->key < b->key)
{a->ch[]=merge(a->ch[],b);a->update();return a;}
else
{b->ch[]=merge(a,b->ch[]);b->update();return b;}
}

对于两棵子树a和b,我们可以实现把b树合并到a树中

在合并时,我们首先看他们的根节点谁的键值比较小(我维护的是一个小根堆),并且建立对应的父子关系。

又由于平衡树的中序遍历不变,我们又要把b插在a后面,维持一个确定的中序遍历,

所以我们应该一直把a作为merge函数的前一个参数,b作为后一个参数,这个顺序不能换.

这一个确定的顺序的重要性尤其体现在后续的区间操作中。刚开始的时候可以当板子背下来,但随着打题肯定会逐渐理解。

接下来我们介绍split函数,这也是一个递归实现的过程,还是先看代码:

 typedef pair<Treap*,Treap*> D;
D split(Treap *o,int k)
{
if(o==null) return D(null,null);
D y;pushdown(o);
if(o->ch[]->size>=k)
{y=split(o->ch[],k);o->ch[]=y.second;o->update();y.second=o;}
else
{y=split(o->ch[],k-o->ch[]->size-);o->ch[]=y.first;o->update();y.first=o;}
return y;
}

我们首先定义一个pair,这样做的好处是同时返回分裂出来的两棵树的根节点指针,我规定第一个是分离完成的树,第二个是剩下的原树。

然后考虑分离前k个的过程:如果o的左儿子有k个以上节点,我们显然应该去左儿子分离。

然后我们会得到分离完成的树和左儿子剩下的树,这时候把左儿子剩下的部分接回节点o,并把新的o作为分离o剩下的原树

如果左儿子节点个数不够,我们就去右儿子分离,过程是相似的,但略有不同,留给读者思考。

有了这两个函数,我们就可以用他们实现一些常用的操作了,比如:

insert=split+newnode+merge+merge

delete=split+split+merge(合并第一个split的first和第二个的second)

等等,其他操作也可以用类似的思路打出来。下面我们用一道例题实战一下。建议读者自己实现代码并充分思考后再核对标程。

3224: Tyvj 1728 普通平衡树

Time Limit: 10 Sec  Memory Limit: 128 MB

Description

您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)

Input

第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

Output

对于操作3,4,5,6每行输出一个数,表示对应答案

Sample Input

10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598

Sample Output

106465
84185
492737

HINT

1.n的数据范围:n<=100000
2.每个数的数据范围:[-2e9,2e9]
 
题解:
这道题本质上只比上面讲的基本操作多了两个函数:查询某个权值的排名和查询某个排名的权值
查询某个权值的排名很简单,在树中递归询问即可
而对于某个权值的排名,我们可以考虑split前k-1个节点,再对第一次split的second进行split,得到第k个节点,并且返回权值
前驱和后继只是上面这两个操作的简单变形,但稍微需要注意一下边界的处理。
代码见下:
 #include <cstdio>
#include <algorithm>
#include <cstring>
#include <ctime>
#include <cstdlib>
using namespace std;
const int maxn=,inf=0x7fffffff;
struct Treap
{
Treap* ch[];
int key,val,size;
Treap(int v)
{size=,val=v,key=rand();ch[]=ch[]=NULL;}
inline void tain()
{size=+(ch[]?ch[]->size:)+(ch[]?ch[]->size:);}
}*root;
typedef pair<Treap*,Treap*> D;
inline int size(Treap *o){return o?o->size:;}
Treap *Merge(Treap *a,Treap* b)
{
if(!a)return b;
if(!b)return a;
if(a->key < b->key)
{a->ch[]=Merge(a->ch[],b);a->tain();return a;}
else
{b->ch[]=Merge(a,b->ch[]);b->tain();return b;}
}
D Split(Treap *o,int k)
{
if(!o)return D(NULL,NULL);
D y;
if(size(o->ch[])>=k)
{y=Split(o->ch[],k);o->ch[]=y.second;o->tain();y.second=o;}
else
{y=Split(o->ch[],k-size(o->ch[])-);o->ch[]=y.first;o->tain();y.first=o;}
return y;
}
int Getkth(Treap *o,int v)
{
if(o==NULL)return ;
return(o->val>=v)?Getkth(o->ch[],v):Getkth(o->ch[],v)+size(o->ch[])+;
}
inline int Findkth(int k)
{
D x=Split(root,k-);
D y=Split(x.second,);
Treap *ans=y.first;
root=Merge(Merge(x.first,ans),y.second);
return ans!=NULL?ans->val:;
}
inline void Insert(int v)
{
int k=Getkth(root,v);
D x=Split(root,k);
Treap *o=new Treap(v);
root=Merge(Merge(x.first,o),x.second);
}
void Delete(int v)
{
int k=Getkth(root,v);
D x=Split(root,k);
D y=Split(x.second,);
root=Merge(x.first,y.second);
}
int main(){
int m,opt,x;scanf("%d",&m);
while(m--)
{
scanf("%d%d",&opt,&x);
switch(opt)
{
case :Insert(x);break;
case :Delete(x);break;
case :printf("%d\n",Getkth(root,x)+);break;
case :printf("%d\n",Findkth(x));break;
case :printf("%d\n",Findkth(Getkth(root,x)));break;
case :printf("%d\n",Findkth(Getkth(root,x+)+));break;
}
}
}
 

[您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)的更多相关文章

  1. [您有新的未分配科技点] 无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )

    1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MB Description Input 输入的第1 行包含两个数N 和M(M ≤20 ...

  2. [您有新的未分配科技点]可,可,可持久化!?------0-1Trie和可持久化Trie普及版讲解

    这一次,我们来了解普通Trie树的变种:0-1Trie以及在其基础上产生的可持久化Trie(其实,普通的Trie也可以可持久化,只是不太常见) 先简单介绍一下0-1Trie:一个0-1Trie节点只有 ...

  3. [您有新的未分配科技点]博弈论进阶:似乎不那么恐惧了…… (SJ定理,简单的基础模型)

    这次,我们来继续学习博弈论的知识.今天我们会学习更多的基础模型,以及SJ定理的应用. 首先,我们来看博弈论在DAG上的应用.首先来看一个小例子:在一个有向无环图中,有一个棋子从某一个点开始一直向它的出 ...

  4. [您有新的未分配科技点]博弈论入门:被博弈论支配的恐惧(Nim游戏,SG函数)

    今天初步学习了一下博弈论……感觉真的是好精妙啊……希望这篇博客可以帮助到和我一样刚学习博弈论的同学们. 博弈论,又被称为对策论,被用于考虑游戏中个体的预测行为和实际行为,并研究他们的应用策略.(其实这 ...

  5. [您有新的未分配科技点]数位DP:从板子到基础(例题 bzoj1026 windy数 bzoj3131 淘金)

    只会统计数位个数或者某种”符合简单规律”的数并不够……我们需要更多的套路和应用 数位dp中常用的思想是“分类讨论”思想.下面我们就看一道典型的分类讨论例题 1026: [SCOI2009]windy数 ...

  6. [您有新的未分配科技点]数位dp:从懵X到板子(例题:HDU2089 不要62)

    数位dp主要用来处理一系列需要数数的问题,一般套路为“求[l,r]区间内满足要求的数/数位的个数” 要求五花八门……比如“不出现某个数字序列”,“某种数的出现次数”等等…… 面对这种数数题,暴力的想法 ...

  7. [您有新的未分配科技点][BZOJ3545&BZOJ3551]克鲁斯卡尔重构树

    这次我们来搞一个很新奇的知识点:克鲁斯卡尔重构树.它也是一种图,是克鲁斯卡尔算法求最小生成树的升级版首先看下面一个问题:BZOJ3545 Peaks. 在Bytemountains有N座山峰,每座山峰 ...

  8. [转载]无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )

    转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html 1500: [NOI2005]维修数列 Time Limit: 10 Sec  Mem ...

  9. [BZOJ3223]文艺平衡树 无旋Treap

    3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个 ...

随机推荐

  1. python3中socket套接字的编码问题解决

    一.TCP 1.tcp服务器创建 #创建服务器 from socket import * from time import ctime #导入ctime HOST = '' #任意主机 PORT = ...

  2. Example015实现html中checkbox的全选和反选(2)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. Ext 创建workspace package

    Ext 创建workspace package Package ExtJs Project 1. 创建工作区间文件目录 md wpt 2. 进入目录 cd wpt 3. 创建 创建工作区间 sench ...

  4. tomcat 修改为自己项目界面

    修改Tomcat欢迎界面为自己项目界面 Posted on 2011-04-16 13:10 IceWee 阅读(1062) 评论(0)  编辑  收藏 所属分类: Tomcat  由于项目要发布到互 ...

  5. 这个类复制文本文件FileCopy

    package JBJADV003; import java.io.File;import java.io.BufferedReader;import java.io.BufferedWriter;i ...

  6. linux可用更新源

    Kali 2.0更新源kali-rolling:#中科大deb http://mirrors.ustc.edu.cn/kali kali-rolling main non-free contribde ...

  7. 了解Java基础原理

    Java 是1995年SUN公司推出的一门高级编程语言,是面向互联网的语言,WEB应用程序首选的语言(安卓底层,大数据hadoop框架用java编写,Spark用Scala编写,Scala用java写 ...

  8. 或许你不知道的10条SQL技巧(转自58沈剑原创)

    这几天在写索引,想到一些有意思的TIPS,希望大家有收获. 一.一些常见的SQL实践 (1)负向条件查询不能使用索引 select * from order where status!=0 and s ...

  9. webpack热更新问题和antd design字体图标库扩展

    标题也不知道怎么写好,真是尴尬.不过话说回来,距离上一次写文快两个月了,最近有点忙,一直在开发新项目, 今天刚刚闲下来,项目准备提测.借这个功夫写点东西,把新项目上学到的一些好的干活分享一下,以便之后 ...

  10. electron 使用 node-ffi C++ 动态链接库(DLL)

    一.为什么需要使用DLL 需要使用系统 API 操作或扩展应用程序: 需要调用第三方的接口API,特别是与硬件设备进行通信,而这些接口 API 基本上都是通过 C++ 动态链接库(DLL)实现的: 需 ...