一、简介

fhq treap 与一般的treap主要有3点不同

1、不用旋转

2、以merge和split为核心操作,通过它们的组合实现平衡树的所有操作

3、可以可持久化

二、核心操作

代码中val表示节点权值,pri表示节点的优先级,维护小根堆

1、split

将1个treap分裂为两个treap

分裂主要有两种:以权值k作为分界点、以位置k作为分界点

①以权值k作为分界点

设原来的treap根节点为root,分裂后的<=k的treap A 的根节点为x,>k的treap B 的根节点为y

当前节点指针now从root开始,在treap A 的x位置加点,treap B 的y位置加点

若now的权值<=k,那么以now为根的左子树全部包含在A中,

把now给x,去now的右子树,x变成x的右子树,y还是y

若now的权值>k,那么以now为分的右子树全部包含在B中,

把now给y,去now的左子树,x还是x,y变成y的左子树

void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(val[now]<=k)
{
x=now;
split(ch[now][],k,ch[now][],y);
}
else
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
update(now);
}
}

②以位置k作为分界点

设原来的treap根节点为root,分裂后的前k个节点的treap A 的根节点为x,剩下的节点的treap B 的根节点为y

判断放在哪颗treap的标准改成k和子树大小的比较,原理同上

void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(k<=siz[ch[now][]])
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
else
{
x=now;
split(ch[now][],k-siz[ch[now][]]-,ch[now][],y);
}
update(now);
}
}

2、merge

合并以x为根的treap A 和 以y为根的treap B

前提:A的权值全部<B的权值

注意在merge的过程中,维护好堆和二叉搜索树的性质

若优先级x<y,

维护堆的性质-->y成为x的子节点

维护二叉搜索树的性质-->y在x的右子树

若优先级x>y,

维护堆的性质-->x成为y的子节点

维护二叉搜索树的性质-->x在y的左子树

int merge(int x,int y)
{
if(!x || !y) return x+y;
if(pri[x]<pri[y])
{
ch[x][]=merge(ch[x][],y);
update(x);
return x;
}
else
{
ch[y][]=merge(x,ch[y][]);
update(y);
return y;
}
}

三、单点操作

1、建树

建树有两种方式:nlogn 一个一个插、n 直接建

这里主要说后者

每次取mid作为当前子树的根节点即可

int new_node(int v)
{
siz[++tot]=;
val[tot]=v;
pri[tot]=rand();
return tot;
} int build(int l,int r)
{
if(l>r) return ;
int mid=l+r>>,v=val[mid];
int now=new_node(v);
ch[now][]=build(l,mid-);
ch[now][]=build(mid+,r);
update(now);
return now;
}

2、插入

添加权值为v的点

1、按权值以v为分界点,split成两棵treapA(<=v),B( >v )

2、合并A和v,得到C

3、合并C和B

merge要求x的权值<y决定了 合并的顺序

split(root,v,x,y);
root=merge(merge(x,new_node(v)),y);

3、删除

删除一个权值为v的点

1、按权值以v为分界点,split成两棵treap A(<=v),B( >v)

2、把A按权值以v-1为分界点,split成两棵treap C(<=v-1) ,D(=v)

3、合并D的根节点的左右子树,相当于删除根节点,得到treap E

4、合并C和E,得F

5、合并F和B

split(root,v,x,z);
split(x,v-,x,y);
y=merge(ch[y][],ch[y][]);
root=merge(merge(x,y),z);

4、查询v的排名

1、按权值以v-1为分界点,split成两棵treap A(<=v-1)  B(>=v)

2、A的大小+1 即为权值v的排名

3、合并A、B

split(root,v-,x,y);
cout<<siz[x]+<<'\n';
root=merge(x,y);

5、查询排名为x的数

int get_kth(int now,int k)
{
while()
{
if(k<=siz[ch[now][]]) now=ch[now][];
else
{
k-=siz[ch[now][]];
if(k==) return now;
k--;
now=ch[now][];
}
}
} cout<<val[get_kth(root,v)]<<'\n';

6、查找比v小的最大的数(前驱)

1、按权值以v-1为分界点,split成两棵treap A(<=v-1),B(>=v)

2、在A中查找A的排名最靠后的数,输出

3、合并A和B

split(root,v-,x,y);
cout<<val[get_kth(x,siz[x])]<<'\n';
root=merge(x,y);

7、查询比v大的最小的数(后继)

1、按权值以v为分界点,split成两棵treap A(<=v),B(>v)

2、在B中找最靠前的数,输出

3、合并A和B

split(root,v,x,y);
cout<<val[get_kth(y,)]<<'\n';
root=merge(x,y);

四、区间操作

对区间[l,r]执行某种操作

1、以位置r作为分界点,split成两棵treap A(前r个节点)、B(位置r之后的节点)

2、在A中以位置l-1为分界点,split成两棵treap C(前l-1个节点)、D(第l到第r个节点)

3、对D执行操作

注意因为执行区间操作,增加2个虚拟节点,最前面一个,最后面一个,所有节点后移一位

加虚拟节点的原因是 如果对区间[1,x]或[x,n]操作,那我们需要用到1-1 号 点和n+1号店

split(root,r+,a,b);
split(a,l,c,d);
work(d)

五、模板

1、普通平衡树

https://www.luogu.org/problemnew/show/P3369

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream> using namespace std; #define N 100001 int root; int tot;
int fa[N],ch[N][],val[N],pri[N],siz[N]; void read(int &x)
{
x=; int f=; char c=getchar();
while(!isdigit(c)) { if(c=='-') f=-; c=getchar(); }
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
x*=f;
} void update(int x)
{
siz[x]=siz[ch[x][]]+siz[ch[x][]]+;
} int new_node(int v)
{
siz[++tot]=;
val[tot]=v;
pri[tot]=rand();
return tot;
} int merge(int x,int y)
{
if(!x || !y) return x+y;
if(pri[x]<pri[y])
{
ch[x][]=merge(ch[x][],y);
update(x);
return x;
}
else
{
ch[y][]=merge(x,ch[y][]);
update(y);
return y;
}
} void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(val[now]<=k)
{
x=now;
split(ch[now][],k,ch[now][],y);
}
else
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
update(now);
}
} int get_kth(int now,int k)
{
while()
{
if(k<=siz[ch[now][]]) now=ch[now][];
else
{
k-=siz[ch[now][]];
if(k==) return now;
k--;
now=ch[now][];
}
}
} int main()
{
srand(time()+);
int n,u,v;
int x,y,z;
read(n);
while(n--)
{
read(u); read(v);
if(u==)
{
split(root,v,x,y);
root=merge(merge(x,new_node(v)),y);
}
else if(u==)
{
split(root,v,x,z);
split(x,v-,x,y);
y=merge(ch[y][],ch[y][]);
root=merge(merge(x,y),z);
}
else if(u==)
{
split(root,v-,x,y);
cout<<siz[x]+<<'\n';
root=merge(x,y);
}
else if(u==)
{
cout<<val[get_kth(root,v)]<<'\n';
}
else if(u==)
{
split(root,v-,x,y);
cout<<val[get_kth(x,siz[x])]<<'\n';
root=merge(x,y);
}
else
{
split(root,v,x,y);
cout<<val[get_kth(y,)]<<'\n';
root=merge(x,y);
}
}
}

2、文艺平衡树

https://www.luogu.org/problemnew/show/P3391

#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<iostream> using namespace std; #define N 100005 int n; int tot,root; int siz[N],ch[N][],val[N],pri[N];
bool rev[N]; void read(int &x)
{
x=; int f=; char c=getchar();
while(!isdigit(c)) { if(c=='-') f=-; c=getchar(); }
while(isdigit(c)) { x=x*+c-''; c=getchar(); }
x*=f;
} int new_node(int v)
{
siz[++tot]=;
val[tot]=v;
pri[tot]=rand();
return tot;
} void update(int x)
{
siz[x]=+siz[ch[x][]]+siz[ch[x][]];
} int build(int l,int r)
{
if(l>r) return ;
int mid=l+r>>,v=mid-;
int now=new_node(v);
ch[now][]=build(l,mid-);
ch[now][]=build(mid+,r);
update(now);
return now;
} void down(int x)
{
rev[x]^=;
swap(ch[x][],ch[x][]);
rev[ch[x][]]^=;
rev[ch[x][]]^=;
} int merge(int x,int y)
{
if(!x || !y) return x+y;
if(rev[x]) down(x);
if(rev[y]) down(y);
if(pri[x]<pri[y])
{
ch[x][]=merge(ch[x][],y);
update(x);
return x;
}
else
{
ch[y][]=merge(x,ch[y][]);
update(y);
return y;
}
} void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
if(rev[now]) down(now);
if(k<=siz[ch[now][]])
{
y=now;
split(ch[now][],k,x,ch[now][]);
}
else
{
x=now;
split(ch[now][],k-siz[ch[now][]]-,ch[now][],y);
}
update(now);
}
} void reverse(int l,int r)
{
int a,b,c,d;
split(root,r+,a,b);
split(a,l,c,d);
rev[d]^=;
root=merge(merge(c,d),b);
} void dfs(int x)
{
if(rev[x]) down(x);
if(ch[x][]) dfs(ch[x][]);
if(val[x]>= && val[x]<=n) cout<<val[x]<<' ';
if(ch[x][]) dfs(ch[x][]);
} int main()
{
srand(time()+);
int m;
read(n); read(m);
root=build(,n+);
int l,r;
while(m--)
{
read(l); read(r);
reverse(l,r);
}
dfs(root);
}

浅谈fhq treap的更多相关文章

  1. 浅谈BST(二叉查找树)

    目录 BST的性质 BST的建立 BST的检索 BST的插入 BST求前驱/后继 BST的节点删除 复杂度 平衡树 BST的性质 树上每个节点上有个值,这个值叫关键码 每个节点的关键码大于其任意左侧子 ...

  2. 在平衡树的海洋中畅游(四)——FHQ Treap

    Preface 关于那些比较基础的平衡树我想我之前已经介绍的已经挺多了. 但是像Treap,Splay这样的旋转平衡树码亮太大,而像替罪羊树这样的重量平衡树却没有什么实际意义. 然而类似于SBT,AV ...

  3. 浅谈 Fragment 生命周期

    版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...

  4. 浅谈 LayoutInflater

    浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...

  5. 浅谈Java的throw与throws

    转载:http://blog.csdn.net/luoweifu/article/details/10721543 我进行了一些加工,不是本人原创但比原博主要更完善~ 浅谈Java异常 以前虽然知道一 ...

  6. 浅谈SQL注入风险 - 一个Login拿下Server

    前两天,带着学生们学习了简单的ASP.NET MVC,通过ADO.NET方式连接数据库,实现增删改查. 可能有一部分学生提前预习过,在我写登录SQL的时候,他们鄙视我说:“老师你这SQL有注入,随便都 ...

  7. fhq treap最终模板

    新学习了fhq treap,厉害了 先贴个神犇的版, from memphis /* Treap[Merge,Split] by Memphis */ #include<cstdio> # ...

  8. 浅谈WebService的版本兼容性设计

    在现在大型的项目或者软件开发中,一般都会有很多种终端, PC端比如Winform.WebForm,移动端,比如各种Native客户端(iOS, Android, WP),Html5等,我们要满足以上所 ...

  9. 浅谈angular2+ionic2

    浅谈angular2+ionic2   前言: 不要用angular的语法去写angular2,有人说二者就像Java和JavaScript的区别.   1. 项目所用:angular2+ionic2 ...

随机推荐

  1. 杂谈---小故事小道理,面试中的小技巧(NO.2)

    本篇是接着上一篇面试随笔的,上一次有猿友反应写的有些“扯淡”,LZ思来想去最大的原因可能是由于上一章写的全是一些大忌,既然是大忌,那么在现实当中发生的概率还是相对较小的,大部分人还是很少在面试中犯如此 ...

  2. docker之镜像管理命令

    一.docker image 镜像管理命令 指令 描述ls 列出本机镜像build 构建镜像来自Dockerfilehistory 查看镜像历史inspect 显示一个或多个镜像详细信息pull 从镜 ...

  3. npm install的几种命令形式区别

    转自未来与传说.jigetage 我们在使用 npm install 安装模块的时候 ,一般会使用下面这几种命令形式: npm install moduleName # 安装模块到项目目录下 npm ...

  4. 回溯-uva129

    题目链接:https://vjudge.net/problem/UVA-129 题解: 这道题卡了一会儿的时间,一开始最大的问题是如何判断添加了一个字符之后,该字符串是不是一个困难的串,解决办法是:利 ...

  5. Win10环境配置Bitcoin Core节点

    区块链是当下比较火热的技术,我也来蹭下热度,研究一把Bitcoin Core的技术. 入门篇 一.Bitcoin Core安装 1.下载 一般有2种安装方式:源码编译安装 和 下载现成的安装包安装 源 ...

  6. UI Recorder 安装教程(一)

    前言: UI Recorder 是一款零成本UI自动化录制工具,类似于Selenium IDE. UI Recorder 要比Selenium IDE更加强大! UI Recorder 非常简单易用. ...

  7. HTML5遇到的问题

    一.Uncaught SyntaxError: Unexpected identifier 解决办法: Uncaught SyntaxError: Unexpected identifier这个问题, ...

  8. Fast Failure Detection and Recovery in SDN with Stateful Data Plane

    文章名称:Fast Failure Detection and Recovery in SDN with Stateful Data Plane 利用SDN的带状态数据平面进行快速故障检测和恢复 发表 ...

  9. vue 跳转路由传参数用法

    // 组件 a <template> <button @click="sendParams">传递</button> </template ...

  10. PhpStorm 配置本地断点调试

    前言: 有够拖延症的,应该是一年多以前就使用过PhpStorm的debug断点调试了吧,不够过当时是别人帮我配的,我记得还挺复杂.后来重装系统后尝试了配置,好像没成吧,记得当初老师帮我配也没成(... ...