可持久化线段树
可持久化线段树
可持久化线段树可以用于修改、维护序列任何一个历史副本
可持久化线段树1(可持久化数组)
只是个关于可持久化数据结构的引子,使用线段树来实现可持久化数组
我们熟知的线段树是长这样的(蓝色指的是修改节点的路径)
现在,我们既要修改也需要保存历史副本。如果我们强行再开一个数组,肯定会爆空间。事实上,我们如果将这个数组用线段树来维护,会发现只有修改路径上的节点需要新开空间,不收修改影响的节点是没必要新开,于是这张图就诞生了
总之就是给被修改的节点新开一个,且保留它和那些没有修改节点的关系
代码实现和线段树有点差别,具体看代码吧
struct chairman
{
int ls[N],rs[N];
int val[N*2],rt[N*2];
//val[i]代表i号节点的值
//rt[i]代表i号历史版本的根节点
int cnt;//代表尾结点
int build(int l,int r)
{
int root=++cnt;
if(l==r)
{
val[l]=a[l];
return root;
}
int mid=(l+r)/2;
ls[root]=build(l,mid);
rs[root]=build(mid+1,r);
return root;
}
int update(int pre,int l,int r,int x,int c)
{
int root=++cnt;
if(l==r)
{
val[root]=c;
return root;
}
int mid=(l+r)/2;
ls[root]=ls[pre];
rs[root]=rs[pre];
if(x<=mid) ls[root]=update(ls[pre],l,mid,x,c);
else rs[root]=update(rs[pre],mid+1,r,x,c);
return root;
}
int query(int pre,int l,int r,int x)
{
if(l==r)
{
return val[l];
}
int mid=(l+r)/2;
if(x<=mid) return query(ls[pre],l,mid,x);
else return query(rs[pre],mid+1,r,x);
}
};
可持续化线段树2
主要思路是主席树+前缀和
不妨以1 5 2 6 3 7 4这组数据举个例子
首先,这题序列中数值范围过大,先对其进行离散化,将这些数转化成编号
接下来,我们在序列的每个位置建立一棵线段树(当然只是方便理解而已,如果真这么做会爆空间,解决方法下面讲)
而每个位置的线段树代表了什么呢?我们给位置\(i\)的线段树的每个节点赋一个有意义的区间 $ [a,b] $,代表了区间 \([1,i]\)中离散化后编号在\([a,b]\)中的数字的个数
然而听起来很抽象,还是来看例子
对于1位置上的线段树,显然只要把1放进去就行了
然后来到2号位置,需要先复制1号线段树,再插入5
3号位置,复制下2号线段树,再插入2
接下来以此类推啦,最后7位置上的线段树就是这样的了
这套操作给人一种用线段树做前缀和的感觉,显然,对于\((i-1)\)号线段树和\(j\)号线段树,通过对应节点相减,可以得到\(区间[i,j]中,离散化后编号在特定区间内的数有多少个\)
那么查询\([l,r]\)中的\(k\)小值时,我们假想一个把\(j\)号线段树和\(i-1\)对应位置相减得到的线段树,若左子树的权值\(x>=k\),就查询左子树的\(k\)小值,否则查询右子树的\(k-x\)小值
关于爆空间的烦恼?我们发现,这套操作和可持久化线段树1的操作是大同小异的,都是新建版本,部分修改,支持访问历史版本,所以我们也可以用1中的方法开新点,这样就解决了爆空间的问题了
代码(特别感谢Mercury_City大佬)
#include <bits/stdc++.h>
#define debug puts("I love Newhanser forever!!!!!");
#define pb push_back
using namespace std;
template <typename T>inline void read(T& t){
t=0; register char ch=getchar(); register int fflag=1;
while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
const int MAXN=10000086;
int n,m,root[MAXN],val[MAXN],a[MAXN],b[MAXN],tot,c[MAXN];
struct Segment_Tree{
int lc[MAXN],rc[MAXN],cnt;
#define Mid ((l+r)>>1)
#define lson lc[u],l,Mid
#define rson rc[u],Mid+1,r
void build(int &u,int l,int r){
u=++cnt;
if(l==r)return;
build(lson);build(rson);
}
void update(int &u,int l,int r,int pre,int pos,int v){
u=++cnt; lc[u]=lc[pre]; rc[u]=rc[pre]; val[u]=val[pre]+v;
if(l==r) return;
if(pos<=Mid) update(lson,lc[pre],pos,v);
else update(rson,rc[pre],pos,v);
}
int Query(int l,int r,int r1,int r2,int k){
if(l==r) return l;
int tmp=val[lc[r2]]-val[lc[r1]];
if(tmp>=k) return Query(l,Mid,lc[r1],lc[r2],k);
else return Query(Mid+1,r,rc[r1],rc[r2],k-tmp);
}
}seg;
int main(){
read(n,m);
for(int i=1;i<=n;++i) read(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
for(int i=1;i<=n;++i) if(i==1||b[i]!=b[i-1]) c[++tot]=b[i];
seg.build(root[0],1,tot);
for(int i=1;i<=n;++i) a[i]=lower_bound(c+1,c+tot+1,a[i])-c;
for(int i=1;i<=n;++i) seg.update(root[i],1,tot,root[i-1],a[i],1);
while(m--){
int x,y,v;
read(x,y,v);
cout<<c[seg.Query(1,tot,root[x-1],root[y],v)]<<endl;
}
return 0;
}
本文来自博客园,作者:羊扬羊,转载请注明原文链接:https://www.cnblogs.com/sheepcsy/p/16368713.html