可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)
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[];
}
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)学习(未完待续)的更多相关文章
- 非旋转Treap
Treap是一种平衡二叉树,同时也是一个堆.它既具有二叉查找树的性质,也具有堆的性质.在对数据的查找.插入.删除.求第k大等操作上具有期望O(log2n)的复杂度. Treap可以通过节点的旋 ...
- 平衡树及笛卡尔树讲解(旋转treap,非旋转treap,splay,替罪羊树及可持久化)
在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用 ...
- 非旋Treap及其可持久化
平衡树这种东西,我只会splay.splay比较好理解,并且好打,操作方便. 我以前学过SBT,但并不是很理解,所以就忘了怎么打了. 许多用平衡树的问题其实可以用线段树来解决,我们真正打平衡树的时候一 ...
- [模板] 平衡树: Splay, 非旋Treap, 替罪羊树
简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ...
- 非旋 treap 结构体数组版(无指针)详解,有图有真相
非旋 $treap$ (FHQ treap)的简单入门 前置技能 建议在掌握普通 treap 以及 左偏堆(也就是可并堆)食用本blog 原理 以随机数维护平衡,使树高期望为logn级别, FHQ ...
- 平衡树简单教程及模板(splay, 替罪羊树, 非旋treap)
原文链接https://www.cnblogs.com/zhouzhendong/p/Balanced-Binary-Tree.html 注意是简单教程,不是入门教程. splay 1. 旋转: 假设 ...
- BZOJ3224普通平衡树——非旋转treap
题目: 此为平衡树系列第一道:普通平衡树您需要写一种数据结构,来维护一些数,其中需要提供以下操作:1. 插入x数2. 删除x数(若有多个相同的数,因只删除一个)3. 查询x数的排名(若有多个相同的数, ...
- 旋转/非旋转treap的简单操作
treap(树堆) 是在二叉搜索树的基础上,通过维护随机附加域,使其满足堆性质,从而使树相对平衡的二叉树: 为什么可以这样呢? 因为在维护堆的时候可以同时保证搜索树的性质: (比如当一棵树的一个域满足 ...
- 【bzoj3224】Tyvj 1728 普通平衡树 01Trie姿势+平衡树的四种姿势 :splay,旋转Treap,非旋转Treap,替罪羊树
直接上代码 正所谓 人傻自带大常数 平衡树的几种姿势: AVL Red&Black_Tree 码量爆炸,不常用:SBT 出于各种原因,不常用. 常用: Treap 旋转 基于旋转操作和随机数 ...
随机推荐
- What is the difference between Gradle Build and Gradle Sync?
Gradle Build helps you to compile your Android app into an APK while Gradle Sync will sync up all yo ...
- seam remote 返回的map结构
map结构的数据,js接收到的结构是elements下面的一个 [ {key:***,value:***}, {key:***,value:***} ] 这样子的集合,需要经过下面代码的转换才能重新变 ...
- linux mysql-workbench 创建与正式库表结构一样的表
先在本地创建数据库 字符集选择这个 创建数据库成功 创建与正式库一样的表 step1: 连接正式库,找到要生成的表,导出创建表的sql语句 step2: 找到本地数据库,选择表,在sql执行区域复制s ...
- session_start(): open(/var/lib/php/session/sess_tlrp3cpro7gun9uhno8n6futr3, O_RDWR)
在安装一个网站,结果连接上数据库了但是一直报错.下面贴报错内容和图 错误提示 系统:PHP (Linux) 错误类型:WARNING 内容:session_start(): open(/) 错误位置: ...
- S-HR之OSF
1):getWorkDayCount ->ArrayList data = (ArrayList) com.kingdee.shr.rpts.ctrlreport.osf.OSFExecutor ...
- yum插件
参考文章: http://www.linuxfly.org/post/297/ [root@dnstest07.tbc /home/ahao.mah] #rpm -qa |grep yum yum-p ...
- for循环中索引值和取值的迷惑
利用for循环和range从100——10,倒序讲所有的偶数添加到一个新列表中,然后对列表的元素进行筛选,将能被4整除的数留下来. even = []for i in range(100,9,-1): ...
- Bomb HDU - 3555
Bomb HDU - 3555 求1~n中含有49数的个数 #include<bits/stdc++.h> #define LL long long using namespace std ...
- 准备MPI编程环境——Visual Studio
准备 下载并安装Visual Studio 2017 下载并安装MPI (建议使用MSMPI,相对简单方便一点,可以从微软官网下载获得) 配置 新建空白项目 在该项目中新建源文件 右击项目-> ...
- win10如何进入安全模式的几种方法
首先,说一下安全模式的作用: 安全模式, 用途有很多,常见的作用有以下几点 1. 电脑可能由于安装了某些驱动或者软件,不兼容导致电脑启动不了,可以进入安全模式卸载 2. 电脑中病毒之后,可以进入安全模 ...