转自

无旋版 $Treap$。

只需要两个操作即可达到 $splay$ 的所有功能

1、$split$

它的主要思想就是把一个 $Treap$ 分成两个。

$split$ 操作有两种类型,一种是按照权值分配,一种是按前 k 个分配。

第一种就是把所有小于 k 的权值的节点分到一棵树中,第二种是把前 k 个分到一个树里。

权值版:

 void split(int o,int k,int &x,int &y){ //这里的x,y分别是将以o为根的树切开后第一个新子树的根和第二个新子树的根
if(!o) x=y=;
else {
if(val[o]<=k)
x=o,split(ch[o][],k,ch[o][],y);
else
y=o,split(ch[o][],k,x,ch[o][]);
pushup(o);
}
}

对于我们遍历到每一个点,假如它的权值小于k,那么它的所有左子树,都要分到左边的树里,然后遍历它的右儿子。假如大于k,把它的所有右子树分到右边的树里,遍历左儿子。

因为它的最多操作次数就是一直分到底,效率就是 $O(logn)$。

对于前k个版的,就是像找第k大的感觉。每次减掉sze

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-sze[ch[now][]]-,ch[now][],y);
pushup(now);
}
}

2、$merge$

这个就是把两个 $Treap$ 合成一个,保证第一个的权值小于第二个。

因为第一个 $Treap$ 的权值都比较小,我们比较一下它的 $prio$ (优先级),假如第一个的 $prio$ 小,我们就可以直接保留它的所有左子树,接着把第一个 $Treap$ 变成它的右儿子。反之,我们可以保留第二棵的所有右子树,指针指向左儿子。

你可以把这个过程形象的理解为在第一个 $ Treap$ 的右子树上插入第二个树,也可以理解为在第二个树的左子树上插入第一棵树。因为第一棵树都满足小于第二个树,所以就变成了比较 $prio$ 来确定树的形态。

也就是说,我们其实是遍历了第一个$Treap$ 的根->最大节点,第二个$Treap$的根->最小节点,也就是 $O(logn)$

int merge(int x,int y){
if(!x or !y) return x+y;
if(prio[x]<prio[y]){
ch[x][]=merge(ch[x][],y);
pushup(x);
return x;
}
else{
ch[y][]=merge(x,ch[y][]);
pushup(y);
return y;
}
}

下面我们就可以通过这两个基本的东西实现各种各样的操作了。

3、insert

插入一个权值为 $k$ 的点,把树按照 $k$ 的权值 $split$ 成两个,再 $merge$ 回去。

4、remove

删除权值为 $k$ 的点,把树按照 $k$ 分成两个$a,b$ 再把$a$ 按照 $k-1$ 分成$c,d$。把$d$ 的两个儿子 $merge$起来,再 $merge(merge(c,d),b)$

void remove(int k){
int x,y,z;
split(Root,k,x,y);
split(x,k-,x,z);
z=merge(ch[z][],ch[z][]);
Root=merge(x,merge(z,y));
}

其它见代码

// 普通平衡树 fhq_Treap
// By YoungNeal
#include<cstdio>
#include<cstdlib>
#define N 100005
#define inf 0x3f3f3f3f int Root;
int n,opt,x,tot;
int val[N],prio[N];
int sze[N],ch[N][]; void pushup(int o){
sze[o]=sze[ch[o][]]+sze[ch[o][]]+;
} void split(int o,int k,int &x,int &y){
if(!o) x=y=;
else {
if(val[o]<=k)
x=o,split(ch[o][],k,ch[o][],y);
else
y=o,split(ch[o][],k,x,ch[o][]);
pushup(o);
}
} int merge(int x,int y){
if(!x or !y) return x+y;
if(prio[x]<prio[y]){
ch[x][]=merge(ch[x][],y);
pushup(x);
return x;
}
else{
ch[y][]=merge(x,ch[y][]);
pushup(y);
return y;
}
} int newnode(int v){
sze[++tot]=;
val[tot]=v;
prio[tot]=rand();
return tot;
} void insert(int k){
int x,y;
split(Root,k,x,y);
Root=merge(merge(x,newnode(k)),y);
} void remove(int k){
int x,y,z;
split(Root,k,x,y);
split(x,k-,x,z);
z=merge(ch[z][],ch[z][]);
Root=merge(x,merge(z,y));
} void kthrank(int k){
int x,y;
split(Root,k-,x,y);
printf("%d\n",sze[x]+);
Root=merge(x,y);
} int rank(int o,int k){
if(sze[ch[o][]]==k-) return val[o];
if(sze[ch[o][]]>=k) return rank(ch[o][],k);
return rank(ch[o][],k-sze[ch[o][]]-);
} void prev(int k){
int x,y;
split(Root,k-,x,y);
printf("%d\n",rank(x,sze[x]));
Root=merge(x,y);
} void nxt(int k){
int x,y;
split(Root,k,x,y);
printf("%d\n",rank(y,));
Root=merge(x,y);
} signed main(){
scanf("%d",&n);
while(n--){
scanf("%d%d",&opt,&x);
if(opt==) insert(x);
if(opt==) remove(x);
if(opt==) kthrank(x);
if(opt==) printf("%d\n",rank(Root,x));
if(opt==) prev(x);
if(opt==) nxt(x);
}
return ;
}

5、区间操作

对于翻转区间 $[l,r]$,我们可以先把区间 $[1,l-1]$ $split$ 出来,再把 $[l,r]$ $split$ 出来就行了。注意 $lazy$ 标记及时清除。

// 文艺平衡树 fhp_Treap
// By YoungNeal
#include<ctime>
#include<cstdio>
#include<cstdlib>
#define N 100005 int Root;
int lazy[N];
int n,m,cnt;
int val[N],sze[N];
int ch[N][],prio[N]; void pushup(int o){
sze[o]=sze[ch[o][]]+sze[ch[o][]]+;
} void pushdown(int o){
if(!lazy[o] or !o) return;
ch[o][]^=ch[o][]^=ch[o][]^=ch[o][];
lazy[ch[o][]]^=;
lazy[ch[o][]]^=;
lazy[o]=;
} void split(int o,int k,int &x,int &y){
if(!o) x=y=;
else{
pushdown(o);
if(k>sze[ch[o][]]) x=o,split(ch[o][],k-sze[ch[o][]]-,ch[o][],y);
else y=o,split(ch[o][],k,x,ch[o][]);
pushup(o);
}
} int merge(int x,int y){
if(!x or !y) return x+y;
pushdown(x); pushdown(y);
if(prio[x]<prio[y]){
ch[x][]=merge(ch[x][],y);
pushup(x);
return x;
}
else{
ch[y][]=merge(x,ch[y][]);
pushup(y);
return y;
}
} int newnode(int v){
val[++cnt]=v;
sze[cnt]=;
prio[cnt]=rand();
return cnt;
} void res(int l,int r){
int a,b,c,d;
split(Root,r,a,b);
split(a,l-,c,d);
lazy[d]^=;
Root=merge(merge(c,d),b);
} void dfs(int now){
if(!now) return;
pushdown(now);
dfs(ch[now][]);
printf("%d ",val[now]);
dfs(ch[now][]);
} signed main(){
srand(time());
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
Root=merge(Root,newnode(i));
//printf("Root=%d\n",Root);
for(int x,y,i=;i<=m;i++){
scanf("%d%d",&x,&y);
res(x,y);
//printf("i=%d\n",i);
//dfs(Root);
}
//printf("Root=%d\n",Root);
dfs(Root);
return ;
}

[总结] fhq_Treap 学习笔记的更多相关文章

  1. fhq_treap 学习笔记

    前言:昨天写NOIp2017队列,写+调辗转了3h+,不知道怎么的,就点进了一个神仙的链接,便在今日学习了神仙的fhq_treap. 简介:fhq_treap功能强大,支持splay支持的所有操作,代 ...

  2. Treap与fhq_Treap学习笔记

    1.普通Treap 通过左右旋来维护堆的性质 左右旋是不改变中序遍历的 #include<algorithm> #include<iostream> #include<c ...

  3. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  4. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  5. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  6. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  7. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  8. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  9. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

随机推荐

  1. JavaScript的基础学习

    由js和python想到的: 弱类型语言 js 中的数据在进行算数运算时,会自动转换类型强类型语言 变量的值的数据类型一旦确定,使用时不能改变 动态语言:编译时不知道数据类型,只有在执行时才知道数据类 ...

  2. AtCoder Beginner Contest 075 D - Axis-Parallel Rectangle

    https://beta.atcoder.jp/contests/abc075/tasks/abc075_d 题意: 给出坐标平面上n个点的坐标,要求找到一个面积最小的矩形使得这个矩形的边界加上内部的 ...

  3. 道可道,非常道——详解promise

    promise 出来已久,以前一直使用,没有仔细剖析原理,最近在复习es6的知识,写一下自己对于promise的理解. promise是es6的一种异步编程解决方案,避免频繁的回调函数,增强代码的可阅 ...

  4. Struts(十二):异常处理:exception-mapping元素

    配置当前action的声明异常处理 1.exception-mapping元素中有2个属性 exception:指定需要捕获的异常类型 result:指定一个响应结果,该结果将在捕获到异常时被执行.即 ...

  5. Linux下wget获取ftp下目录下文件

    如果某个目录下有一个文件可以使用ftp命令: get xxx 如果是某个目录下有多个文件(且不需要获取目录下子文件夹下的内容): mget * 如果是某个目录下有子目录希望获取所有子目录: wget ...

  6. Java基础之程序流程控制

    Java中的程序流程控制 Java中的程序流程分为三种结构:①顺序结构:②分支结构:③循环结构 一.顺序结构 Java中定义成员变量的时候,采用的是前向引用,也就是后面的变量可以引用之前定义好的变量. ...

  7. 招募:Wiki 文档翻译小伙伴招募

    https://github.com/dotnetcore/CAP/issues/93 为了推进 CAP 的国际化工作,为全球其他 .NET 开发者提供更加良好的文档阅读体验,现在需要对CAP wik ...

  8. oracle 11g rac R2 for linux change(public,vip)IP ,hostname (oracle 11g rac R2 修改公有,虚拟,私有IP,网卡)

    结构如下: 说明: 节点IP,主机名的修改步骤来自ORACLE support文档: 如何修改集群的公网信息(包括 VIP) (文档 ID 1674442.1) 实验环境情况如下: 实验 节点名称 数 ...

  9. IDEA2017.3.4破解方式

    下载jetbrainsCrack-2.7-release-str.jar包 下载地址: https://files.cnblogs.com/files/xifenglou/JetBrains.zip ...

  10. WKWebView和WebView与JS的交互方式

    UIWebView与JS的交互方式 一,OC调用JS直接调用苹果提供的API - (nullable NSString *)stringByEvaluatingJavaScriptFromString ...