fhq_treap 学习笔记
前言:昨天写NOIp2017队列,写+调辗转了3h+,不知道怎么的,就点进了一个神仙的链接,便在今日学习了神仙的fhq_treap。
简介:fhq_treap功能强大,支持splay支持的所有操作,代码简单,仅有两个核心函数\(merge\)和\(split\),更重要的是,它作为无旋的平衡树,是支持可持久化的。
正题:fhq_treap的实现
(0)树的域
对树的每个节点,我们维护
\(ch[2],siz,dat,val\)
分别为左右儿子,子树大小,BST性质的关键字,堆性质的关键字
其中堆是小根堆
(1)split函数
\(void \ split(now,k,\&x,\&y)\)
按照\(k\)值的意义可以有两种方式理解,其一为把以\(now\)为根的子树按照\(BST\)性质的权值按不大于\(k\)和大于\(k\)两种分裂成两颗子树,两颗子树的根分别为\(x\)和\(y\)。
这个函数是递归执行的,有严格的子问题划分性。
我们可以这样描述一次对整棵树的操作:
比较当前根节点权值\(dat[now]\)与\(k\)值
若大于,则根节点和右子树被划分为\(y\)树,当前根为\(now\),\(y\)树的左子树不确定,进入左子树求解子问题
若不大于,则根节点和左子树被划分为\(x\)树,当前根为\(now\),\(x\)树的右子树不确定,进入右子树求解子问题
复杂度分析:进入节点所构成的路径相当于对平衡树做了一次查询值为\(k\)的点的操作,复杂度与树的深度相关,根据\(treap\)的随机性,可以认为是\(O(logn)\)
代码实现:
void split(int now,int k,int &x,int &y)//把小于等于k的树分在x上
{
if(!now) {x=y=0;return;}
if(dat[now]>k) y=now,split(ls,k,x,ls);
else x=now,split(rs,k,rs,y);
updata(now);
}
其中,\(ls\),\(rs\)为宏定义的左右儿子,\(updata\)为更新节点的大小\(siz\)
另一种\(k\)值的意义为树中排名为\(k\)的节点,实现起来差不多
(2)merge函数
\(int \ merge(int \ x,int \ y)\)
意义为:把以\(x\)为根的子树和以\(y\)值为根的子树合并,返回新根节点。
注意这里有要求:以\(x\)值为根的子树的BST性质的最大节点小于以\(y\)为根的子树的最小节点。也就是说,这两棵子树本来就是在BST是一颗完全小于另外一颗,我们只需要在合并时保证堆性质即可。
这个函数是仍然递归执行的,有严格的子问题划分性。
我们可以这样描述一次对整棵树的操作:
比较两个根节点的\(val\)
若\(val[x]<val[y]\),好的当前根就是\(x\)了,\(x\)的左子树不用管了,我们进入\(x\)的右子树和\(y\)继续玩
否则,差不多啊反过来就行了
复杂度分析:两颗子树你一下我一下的走到了底,复杂度与子树的深度之和相关,可以认为是\(O(logn)\)
代码实现:
int Merge(int x,int y)//左树的权值小于右树
{
if(!x||!y) return x+y;
if(val[x]<val[y])
{
ch[x][1]=Merge(ch[x][1],y);
updata(x);
return x;
}
else
{
ch[y][0]=Merge(x,ch[y][0]);
updata(y);
return y;
}
}
(3)其他常见操作
有了功能强大的\(split\)和\(merge\),我们就可以很轻松的执行其他操作了
\(insert(k)\)
实现:把树按\(k\)分裂成两个,新建一个点再合并回去
代码:
void Insert(int k)
{
int x,y;
split(root,k,x,y);
root=Merge(Merge(x,New(k)),y);
}
\(extrack(k)\)//删除
实现:把树先分裂成两个,再把含\(k\)的那一颗把\(k\)单独分出来。注意如果有重复元素,单独的\(k\)构成的子树只删除根节点就够了。最后合并回去。
代码:
void extrack(int k)
{
int x,y,z;
split(root,k,x,y);
split(x,k-1,x,z);
z=Merge(ch[z][0],ch[z][1]);
root=Merge(x,Merge(z,y));
}
(4) 实例 洛谷P3369普通平衡树
Code:
#include <cstdio>
#include <cstdlib>
#define ls ch[now][0]
#define rs ch[now][1]
const int N=100010;
int siz[N],ch[N][2],dat[N],val[N],tot,root;
void updata(int now)
{
siz[now]=siz[ls]+siz[rs]+1;
}
void split(int now,int k,int &x,int &y)//把小于等于k的树分在x上
{
if(!now) {x=y=0;return;}
if(dat[now]>k) y=now,split(ls,k,x,ls);
else x=now,split(rs,k,rs,y);
updata(now);
}
int Merge(int x,int y)//左树的权值小于右树
{
if(!x||!y) return x+y;
if(val[x]<val[y])
{
ch[x][1]=Merge(ch[x][1],y);
updata(x);
return x;
}
else
{
ch[y][0]=Merge(x,ch[y][0]);
updata(y);
return y;
}
}
int New(int k)
{
siz[++tot]=1;dat[tot]=k;val[tot]=rand();
return tot;
}
void Insert(int k)
{
int x,y;
split(root,k,x,y);
root=Merge(Merge(x,New(k)),y);
}
void extrack(int k)
{
int x,y,z;
split(root,k,x,y);
split(x,k-1,x,z);
z=Merge(ch[z][0],ch[z][1]);
root=Merge(x,Merge(z,y));
}
void Rank(int k)
{
int x,y;
split(root,k-1,x,y);
printf("%d\n",siz[x]+1);
root=Merge(x,y);
}
void frank(int now,int x)
{
while(233)
{
if(siz[ls]>=x) now=ls;
else if(siz[ls]+1<x) x-=siz[ls]+1,now=rs;
else {printf("%d\n",dat[now]);return;}
}
}
void pre(int k)
{
int x,y;
split(root,k-1,x,y);
frank(x,siz[x]);
root=Merge(x,y);
}
void suc(int k)
{
int x,y;
split(root,k,x,y);
frank(y,1);
root=Merge(x,y);
}
int main()
{
int t,opt,x;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&opt,&x);
if(opt==1) Insert(x);
else if(opt==2) extrack(x);
else if(opt==3) Rank(x);
else if(opt==4) frank(root,x);
else if(opt==5) pre(x);
else suc(x);
}
return 0;
}
2018.7.28
fhq_treap 学习笔记的更多相关文章
- [总结] fhq_Treap 学习笔记
无旋版 $Treap$. 只需要两个操作即可达到 $splay$ 的所有功能 1.$split$ 它的主要思想就是把一个 $Treap$ 分成两个. $split$ 操作有两种类型,一种是按照权值分配 ...
- Treap与fhq_Treap学习笔记
1.普通Treap 通过左右旋来维护堆的性质 左右旋是不改变中序遍历的 #include<algorithm> #include<iostream> #include<c ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
- JAVA GUI编程学习笔记目录
2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...
- seaJs学习笔记2 – seaJs组建库的使用
原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...
- CSS学习笔记
CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...
随机推荐
- 为什么测试人员必须掌握Linux?
相信点进来的小伙伴不是对Linux感兴趣就是对测试感兴趣了,也希望本文可以帮助之前接触过Linux的小伙伴找到继续坚持学习下去的动力,之前没接触过Linux的小伙伴也能找到开始学习Linux的兴趣. ...
- lintcode702 连接两个字符串中的不同字符
连接两个字符串中的不同字符 给出两个字符串, 你需要修改第一个字符串,将所有与第二个字符串中相同的字符删除, 并且第二个字符串中不同的字符与第一个字符串的不同字符连接 思路:遍历两个字符串,找到互 ...
- 使用bing或google来翻译网页
google代码:在</head>之后插入 <meta name="google-translate-customization" content="4 ...
- opencv-学习笔记(2)
opencv-学习笔记(2) 这章记录了 获取像素点,改变像素点 获取图像的属性(行,列,通道数,数据类型) roi感应区 拆分以及合并图像通道 边缘扩充 opencv获取像素点,改变像素点 ---- ...
- 十一:Centralized Cache Management in HDFS 集中缓存管理
集中的HDFS缓存管理,该机制可以让用户缓存特定的hdfs路径,这些块缓存在堆外内存中.namenode指导datanode完成这个工作. Centralized cache management i ...
- JavaScript初探系列之基本概念
JavaScript的核心语言特性在ECMA-262中是以名为ECMAScript(ECMA, EuropeanComputer Manufacturers Association )的伪语言的形式来 ...
- 计算器软件实现系列(六)windowform窗体+SQL+策略模式
一 整体概述 这个计算器软件的功能和以前的功能基本上一样,只不过是数据的保存形式发生了变化,,以前用的是txt文件保存,现在更正用SQL数据库,现在更改了以前的文件保存形式,是三层架构中数据层的更换, ...
- 寒假作业end
开始写博客的个人体会 自己打的链表过不了,果然,心存侥幸是不行的,被揪出来也不错,很感谢畅畅酱. 学术诚信的重要性 爱因斯坦说过:"大多数人说是才智造就了伟大的科学家,他们错了,是人格.&q ...
- android在程序崩溃时Catch异常并处理
Android系统的"程序异常退出",给应用的用户体验造成不良影响.为了捕获应用运行时异常并给出友好提示,便可继承UncaughtExceptionHandler类来处理.通过Th ...
- 【android】实现手指滑动来切换activity(转)
http://code.eoe.cn/115 1.jpg外部引用 原始文档 MainActivity.java外部引用 原始文档 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...