简介:
    Treap,一种表现优异的BST
优势:
    其较于AVL、红黑树实现简单,浅显易懂
    较于Splay常数小,通常用于树套BST表现远远优于Splay
    或许有人想说SBT,SBT我没有实现过,据说比较快
    但是SBT、Splay以及旋转版Treap等BST都不可以比较方便地实现‘可持久化操作
 
Treap=Tree+Heap
    Treap是一颗同时拥有二叉搜索树和堆性质的一颗二叉树
    Treap有两个关键字,在这里定义为:
        1.key,满足二叉搜索树性质,即中序遍历按照key值有序
        2.fix,满足堆性质,即对于任何一颗以x为根的子树,x的fix值为该子树的最值,方便后文叙述,定义为最小值
    为了满足期望,fix值是一个随机的权值,用来保证树高期望为logn
    剩下的key值则是用来维护我们想要维护的一个权值,此为一个二叉搜索树的基本要素
 
支持操作:
    基本操作:
        1.Build【构造Treap】【O(n)】
        2.Merge【合并】【O(logn)】
        3.Split【拆分】【O(logn)】
        4.Newnode【新建节点】【O(1)】
    可支持操作:
        1.Insert【Newnode+Merge】【O(logn)】
        2.Delete【Split+Split+Merge】【O(logn)】
        3.Find_kth【Split+Split】【O(logn)】
        4.Query【Split+Split】【O(logn)】
        5.Cover【Split+Split+Merge】【O(logn)】
        and more....
 
 
 
先说建树,Treap属于笛卡尔树,所以建树方式同笛卡尔树
用栈维护一个fix值递增的序列即可。
代码
int build(int *data,int n)
{
int x,last=;static int sta[maxn],top;
for(int i=;i<=n;i++)
{
x=new_node(data[i]),last=;
while(top&&fix[sta[top]]>fix[x]) update(sta[top]),last=sta[top],sta[top--]=;
if(top) ch[sta[top]][]=x;
ch[x][]=last;sta[++top]=x;
}
while(top) update(sta[top--]);
return sta[];
}
 
只需要两个基础操作,就可以达到splay的所有功能

1: split

将Treap按照权值或排名分裂为两棵Treap 我只写了按权值分裂

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

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

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);
}
}

2: merge

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

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

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

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

int merge(int A,int B)
{
if(!A||!B) return A+B;
if(fix[A]<fix[B]){ch[A][]=merge(ch[A][],B); update(A); return A;}
else {ch[B][]=merge(A,ch[B][]); update(B); return B;}
}

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

一:基本操作

insertinsert

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

deldel

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

(因为把c的两个儿子merge起来之后,如果权值为v的节点有一个,就已经把他删除了,因为merge后c=0;若有多个就删除了一个)

precursorprecursor

找前驱的话把root按v-1 split成x,y,在x里面找最大值

successorsuccessor

找后继的话把root按v split成x,y,在y里找最小值

rankrank

把root按v-1 split成x,y,排名是x的siz

代码:https://www.luogu.org/problem/show?pid=3369 普通平衡树

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib> #define maxn 500001 using namespace std;
int size[maxn],ch[maxn][],fix[maxn],val[maxn];
int T,cnt,n,m,x,y,z,p,a,root,com; inline int read()
{
int x=,f=;char c=getchar();
while(c>''||c<''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} inline void update(int x)
{
size[x]=+size[ch[x][]]+size[ch[x][]];
}
inline int new_node(int x)
{
size[++cnt]=;
val[cnt]=x;
fix[cnt]=rand();
return cnt;
} int merge(int A,int B)
{
if(!A||!B) return A+B;
if(fix[A]<fix[B]){ch[A][]=merge(ch[A][],B); update(A); return A;}
else {ch[B][]=merge(A,ch[B][]); update(B); return B;}
} 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 kth(int now,int k)
{
while()
{
if(k<=size[ch[now][]]) now=ch[now][];
else if(k==size[ch[now][]]+) return now;
else k-=size[ch[now][]]+,now=ch[now][];
}
} int main()
{
srand((unsigned)time(NULL));
T=read();
while(T--)
{
p=read();a=read();
if(p==)
{
split(root,a,x,y);
root=merge(merge(x,new_node(a)),y);
}
else if(p==)
{
split(root,a,x,z);
split(x,a-,x,y);
y=merge(ch[y][],ch[y][]);
root=merge(merge(x,y),z);
}
else if(p==)
{
split(root,a-,x,y);
printf("%d\n",size[x]+);
root=merge(x,y);
}
else if(p==) printf("%d\n",val[kth(root,a)]);
else if(p==)
{
split(root,a-,x,y);
printf("%d\n",val[kth(x,size[x])]);
root=merge(x,y);
}
else
{
split(root,a,x,y);
printf("%d\n",val[kth(y,)]);
root=merge(x,y);
}
}
return ;
}

二:区间操作

区间提取

分为两次split操作 第一次split(root,pos-1,x1,x2);第二次split(x2,len,y1,y2) y1就是提取出的区间  注意split是按照编号split而不是权值

因此就可以进行一系列的区间操作,如区间改值,区间删除,区间插入等

bzoj1500 维修数列 http://www.lydsy.com/JudgeOnline/problem.php?id=1500

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue> #define maxn 500010
#define inf 0x3f3f3f3f using namespace std;
int siz[maxn],ch[maxn][],fix[maxn],val[maxn],a[maxn],f[maxn];
int tmx[maxn],lmx[maxn],rmx[maxn],sum[maxn],rev[maxn],cov[maxn];
int T,cnt,n,m,b,root,com;
queue<int>trashcan; inline int read()
{
int x=,f=;char c=getchar();
while(c>''||c<''){if(c=='-')f=-;c=getchar();}
while(c>=''&&c<=''){x=x*+c-'';c=getchar();}
return x*f;
} long int random()
{
static int seed=;
return seed=int(seed*48271LL%);
} inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
void swap(int &a,int &b){a^=b;b^=a;a^=b;} int new_node(int v)
{
int x;
if(!trashcan.empty()) x=trashcan.front(),trashcan.pop();
else x=++cnt;
ch[x][]=ch[x][]=rev[x]=;
cov[x]=inf;fix[x]=random();siz[x]=;
val[x]=sum[x]=tmx[x]=v;
lmx[x]=rmx[x]=max(v,);
return x;
} void update(int x)
{
if(ch[x][]&&ch[x][])
{
siz[x]=+siz[ch[x][]]+siz[ch[x][]];
sum[x]=sum[ch[x][]]+sum[ch[x][]]+val[x];
tmx[x]=max(tmx[ch[x][]],tmx[ch[x][]]);
tmx[x]=max(tmx[x],rmx[ch[x][]]+val[x]+lmx[ch[x][]]);
lmx[x]=max(lmx[ch[x][]],sum[ch[x][]]+val[x]+lmx[ch[x][]]);
rmx[x]=max(rmx[ch[x][]],sum[ch[x][]]+val[x]+rmx[ch[x][]]);
}
else if(ch[x][])
{
siz[x]=siz[ch[x][]]+;
sum[x]=sum[ch[x][]]+val[x];
tmx[x]=max(tmx[ch[x][]],rmx[ch[x][]]+val[x]);
lmx[x]=max(lmx[ch[x][]],sum[ch[x][]]+val[x]);
lmx[x]=max(,lmx[x]);
rmx[x]=max(,val[x]+rmx[ch[x][]]);
}
else if(ch[x][])
{
siz[x]=siz[ch[x][]]+;
sum[x]=sum[ch[x][]]+val[x];
tmx[x]=max(tmx[ch[x][]],lmx[ch[x][]]+val[x]);
rmx[x]=max(rmx[ch[x][]],sum[ch[x][]]+val[x]);
rmx[x]=max(,rmx[x]);
lmx[x]=max(,lmx[ch[x][]]+val[x]);
}
else
{
siz[x]=,sum[x]=tmx[x]=val[x];
lmx[x]=rmx[x]=max(val[x],);
}
} void reverse(int x)
{
swap(ch[x][],ch[x][]);
swap(lmx[x],rmx[x]);
rev[x]^=;
} void cover(int x,int v)
{
val[x]=v;sum[x]=siz[x]*v;
lmx[x]=rmx[x]=max(sum[x],);
tmx[x]=max(sum[x],val[x]);
cov[x]=v;
} void pushdown(int x)
{
if(rev[x])
{
if(ch[x][]) reverse(ch[x][]);
if(ch[x][]) reverse(ch[x][]);
}
if(cov[x]!=inf)
{
if(ch[x][]) cover(ch[x][],cov[x]);
if(ch[x][]) cover(ch[x][],cov[x]);
}
rev[x]=;cov[x]=inf;
} int build(int *data,int n)
{
int x,last=;static int sta[maxn],top;
for(int i=;i<=n;i++)
{
x=new_node(data[i]),last=;
while(top&&fix[sta[top]]>fix[x]) update(sta[top]),last=sta[top],sta[top--]=;
if(top) ch[sta[top]][]=x;
ch[x][]=last;sta[++top]=x;
}
while(top) update(sta[top--]);
return sta[];
} void split(int now,int k,int &x,int &y)
{
if(!now) x=y=;
else
{
pushdown(now);
if(siz[ch[now][]]>=k) y=now,split(ch[now][],k,x,ch[now][]);
else x=now,split(ch[now][],k-siz[ch[now][]]-,ch[now][],y);
update(now);
}
} int merge(int A,int B)
{
if(A) pushdown(A); if(B) pushdown(B);
if(A*B==) return A+B;
if(fix[A]<fix[B]){ch[A][]=merge(ch[A][],B);update(A);return A;}
else{ch[B][]=merge(A,ch[B][]);update(B);return B;}
} void trash(int x)
{
if(!x) return;
trashcan.push(x);
trash(ch[x][]); trash(ch[x][]);
} void insert()
{
int pos=read(),len=read(),x,y; static int datas[maxn];
for(int i=;i<=len;i++) datas[i]=read();
int rt=build(datas,len);
split(root,pos,x,y);
root=merge(merge(x,rt),y);
} void del()
{
int pos=read(),len=read(),x1,x2,y1,y2;
split(root,pos-,x1,x2);
split(x2,len,y1,y2);
root=merge(x1,y2); trash(y1);
} void covers()
{
int pos=read(),len=read(),v=read(),x1,x2,y1,y2;
split(root,pos-,x1,x2);
split(x2,len,y1,y2);
cover(y1,v);
root=merge(x1,merge(y1,y2));
} void reverses()
{
int pos=read(),len=read(),x1,x2,y1,y2;
split(root,pos-,x1,x2);
split(x2,len,y1,y2); reverse(y1);
root=merge(x1,merge(y1,y2));
} void sums()
{
int pos=read(),len=read(),x1,x2,y1,y2;
split(root,pos-,x1,x2);
split(x2,len,y1,y2);
printf("%d\n",sum[y1]);
root=merge(x1,merge(y1,y2));
} int main()
{
n=read();m=read();
for(int i=;i<=n;i++)
a[i]=read();
root=build(a,n);
static char s[];
while(m--)
{
scanf("%s",s);
if(s[]=='I') insert();
else if(s[]=='D') del();
else if(s[]=='M'&&s[]=='K') covers();
else if(s[]=='R') reverses();
else if(s[]=='G') sums();
else printf("%d\n",tmx[root]);
}
return ;
}

可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)的更多相关文章

  1. 非旋转Treap

    Treap是一种平衡二叉树,同时也是一个堆.它既具有二叉查找树的性质,也具有堆的性质.在对数据的查找.插入.删除.求第k大等操作上具有期望O(log2n)的复杂度.     Treap可以通过节点的旋 ...

  2. 平衡树及笛卡尔树讲解(旋转treap,非旋转treap,splay,替罪羊树及可持久化)

    在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用 ...

  3. 非旋Treap及其可持久化

    平衡树这种东西,我只会splay.splay比较好理解,并且好打,操作方便. 我以前学过SBT,但并不是很理解,所以就忘了怎么打了. 许多用平衡树的问题其实可以用线段树来解决,我们真正打平衡树的时候一 ...

  4. [模板] 平衡树: Splay, 非旋Treap, 替罪羊树

    简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ...

  5. 非旋 treap 结构体数组版(无指针)详解,有图有真相

    非旋  $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ  ...

  6. 平衡树简单教程及模板(splay, 替罪羊树, 非旋treap)

    原文链接https://www.cnblogs.com/zhouzhendong/p/Balanced-Binary-Tree.html 注意是简单教程,不是入门教程. splay 1. 旋转: 假设 ...

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

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

  8. 旋转/非旋转treap的简单操作

    treap(树堆) 是在二叉搜索树的基础上,通过维护随机附加域,使其满足堆性质,从而使树相对平衡的二叉树: 为什么可以这样呢? 因为在维护堆的时候可以同时保证搜索树的性质: (比如当一棵树的一个域满足 ...

  9. 【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树

    直接上代码 正所谓 人傻自带大常数 平衡树的几种姿势:  AVL Red&Black_Tree 码量爆炸,不常用:SBT 出于各种原因,不常用. 常用: Treap 旋转 基于旋转操作和随机数 ...

随机推荐

  1. Power Designer逆向操作(从mysql5.0生成数据库的物理模型)

    Power Designer逆向操作(从mysql5.0生成数据库的物理模型) 环境:powderdesigner12.5:mysql5.0 步骤: 1.  为指定的数据库配置MySQL的ODBC数据 ...

  2. Xftp 5 和 Xshell 5 基本使用方法

    软件介绍: (1)Xshell: 一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的 TELNET 协议.Xshell通过互联网可以连接到远程的服 ...

  3. ceph部署

    一.部署准备: 准备5台机器(linux系统为centos7.6版本),当然也可以至少3台机器并充当部署节点和客户端,可以与ceph节点共用:     1台部署节点(配一块硬盘,运行ceph-depo ...

  4. vue跨域问题解决(生产环境)

    vue跨域问题解决(使用webpack打包的) 配置代理:(config下index.js文件) module.exports = { dev: { env: require('./dev.env') ...

  5. jquery 对 table 的操作

    <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta ...

  6. Oracle开发常用函数 max 最大数 自动加 1 的模式

    create sequence bs_com_seq increment by 1 start with 1 minvalue 1 maxvalue 999999 cycle nocache orde ...

  7. pip/pip3国内源

    Error 在使用pip3安装PySide2时出现ReadTimeoutError. $ pip3 install PySide2 Solution 使用国内源 例如: $ pip3 install ...

  8. Windows Server 2008安装教程

    系统简介 windows server 2008是迄今为止最灵活.最稳定的windows 操作系统.Windows server 2008 的安装过程是基于镜像文件的,主要版本:Windows Ser ...

  9. 快速搭建vue2.0+boostrap项目

    一.Vue CLI初始化Vue项目 全局安装vue cli npm install --global vue-cli 创建一个基于 webpack 模板的新项目 vue init webpack my ...

  10. Caused by: android.os.TransactionTooLargeException总结

    错误信息 Error: android.os.TransactionTooLargeException W/ActivityManager(344): android.os.TransactionTo ...