学习这篇博客之前需要先了解可持久化线段树是什么,在这附上一篇我之前写的关于可持久化线段树的博客,感兴趣的小伙伴可以看一下。
(67条消息) 可持久化线段树_AC__dream的博客-CSDN博客
在普通线段树上进行区间修改时我们引入了一个lazy数组,而在可持久化线段树上我们引入了lazy数组标记永久化,在普通线段树上有一个pushdown操作,会用lazy数组来更sum数组的值,而在可持久化线段树上则不会通过lazy数组来改变sum数组的值,在可持久化线段树上面,每一个lazy数组只记录当前区间的值应该怎样改变,而我们每次向下搜索区间时都会下传lazy标记,这样就能够得到每个区间的准确的值了,下面我将结合代码进行介绍:
上传操作:
void pushup(int id,int l,int r)
{
//当前区间的值等于左右子节点的值的和加上当前区间的lazy标记
sum[id]=sum[ln[id]]+sum[rn[id]]+(r-l+1)*lazy[id];
}
区间更新操作:
//l和r为当前区间左右端点,L和R为目标区间左右端点
void update_interval(int pre,int id,int L,int R,int val,int l,int r)
{
ln[id]=ln[pre];rn[id]=rn[pre],sum[id]=sum[pre],lazy[id]=lazy[pre];
if(l>=L&&r<=R)
{
lazy[id]+=val;
sum[id]+=(r-l+1)*val;
return ;
}
int mid=l+r>>1;
if(L<=mid) ln[id]=++idx,update_interval(ln[pre],ln[id],L,R,val,l,mid);
if(mid+1<=R) rn[id]=++idx,update_interval(rn[pre],rn[id],L,R,val,mid+1,r);
pushup(id,l,r);
}
区间查询操作:
//l和r为当前区间左右端点,L和R为目标区间左右端点
//询问时需要下传lazy
ll query_interval(int id,int L,int R,ll lz,int l,int r)
{
if(l>=L&&r<=R) return lz*(r-l+1)+sum[id];
int mid=l+r>>1;
ll ans=0;
if(L<=mid) ans+=query_interval(ln[id],L,R,lz+lazy[id],l,mid);
if(mid+1<=R) ans+=query_interval(rn[id],L,R,lz+lazy[id],mid+1,r);
return ans;
}
单点查询操作:
//l,r为当前区间
int query_point(int id,int x,int lz,int l,int r)
{
if(l==r) return sum[id]+lz;
int mid=l+r>>1;
if(x<=mid) return query_point(ln[id],x,lz+lazy[id],l,mid);
else return query_point(rn[id],x,lz+lazy[id],mid+1,r);
}
可持久化线段树的题目一般容易卡空间,我之前书写线段树的习惯是开两个数组,l[],r[],单独记录每个区间的左右端点,但是有时候会MLE,所以我在这建议大家在记忆可持久化线段树的模板时一定要选择一个传参的模板。