可持久化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 旋转 基于旋转操作和随机数 ...
随机推荐
- Power Designer逆向操作(从mysql5.0生成数据库的物理模型)
Power Designer逆向操作(从mysql5.0生成数据库的物理模型) 环境:powderdesigner12.5:mysql5.0 步骤: 1. 为指定的数据库配置MySQL的ODBC数据 ...
- Xftp 5 和 Xshell 5 基本使用方法
软件介绍: (1)Xshell: 一个强大的安全终端模拟软件,它支持SSH1, SSH2, 以及Microsoft Windows 平台的 TELNET 协议.Xshell通过互联网可以连接到远程的服 ...
- ceph部署
一.部署准备: 准备5台机器(linux系统为centos7.6版本),当然也可以至少3台机器并充当部署节点和客户端,可以与ceph节点共用: 1台部署节点(配一块硬盘,运行ceph-depo ...
- vue跨域问题解决(生产环境)
vue跨域问题解决(使用webpack打包的) 配置代理:(config下index.js文件) module.exports = { dev: { env: require('./dev.env') ...
- jquery 对 table 的操作
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta ...
- Oracle开发常用函数 max 最大数 自动加 1 的模式
create sequence bs_com_seq increment by 1 start with 1 minvalue 1 maxvalue 999999 cycle nocache orde ...
- pip/pip3国内源
Error 在使用pip3安装PySide2时出现ReadTimeoutError. $ pip3 install PySide2 Solution 使用国内源 例如: $ pip3 install ...
- Windows Server 2008安装教程
系统简介 windows server 2008是迄今为止最灵活.最稳定的windows 操作系统.Windows server 2008 的安装过程是基于镜像文件的,主要版本:Windows Ser ...
- 快速搭建vue2.0+boostrap项目
一.Vue CLI初始化Vue项目 全局安装vue cli npm install --global vue-cli 创建一个基于 webpack 模板的新项目 vue init webpack my ...
- Caused by: android.os.TransactionTooLargeException总结
错误信息 Error: android.os.TransactionTooLargeException W/ActivityManager(344): android.os.TransactionTo ...