对线段树的lazy标记,分别有“标记下传”与“标记永久化”两种操作思路,上一篇写了“标记下传”,这一篇写“标记永久化”。
“标记永久化”与“标记下传”相比,区别在于“标记永久化”在更新节点时,就一次性将所有需要更改的节点的标记修改完成,查询时无需对标记进行修改。
以查询信息为区间和为例,用“标记永久化”思路进行解题。
存储信息用的数组同样需要两个,sum[]记录区间和,add[]记录区间增加标记。
其中,当修改区间完全包含当前区间时,仅修改add[]值,修改值为单个节点改变量若不完全包含,则仅对sum[]值进行修改,修改值为单个节点改变量×(修改区间与当前区间重合长度),然后继续对该区间的子区间重复上述操作。
按上述思路进行区间修改后,查询思路如下:
当查询区间完全包含当前区间时,直接返回该区间的sum值+该区间add值*该区间长度。
否则,该区间返回值为该区间add值×(查询区间与当前区间重合长度)+左子区间返回值+右子区间返回值
对于两区间重合部分长度,计算方法如下:
区间a[l,r],区间b[x,y]
len=min(y,r)-max(x,l)+1
代码:
void Updata(int o,int l,int r,int x,int y,int k) //区间修改
{
if(x<=l&&y>=r)
{
add[o]+=k;
return;
}
sum[o]+=k*(min(y,r)-max(x,l)+1);
int m=(l+r)>>1;
if(x<=m)Updata(o*2,l,m,x,y,k);
if(y>m)Updata(o*2+1,m+1,r,x,y,k);
}
int Query(int o,int l,int r,int x,int y) //区间查询
{
if(x<=l&&y>=r)return sum[o]+add[o]*(r-l+1);
int m=(l+r)>>1;
int res=add[o]*(min(y,r)-max(x,l)+1);
if(x<=m)res+=Query(o*2,l,m,x,y);
if(y>m)res+=Query(o*2+1,m+1,r,x,y);
return res;
}
区间永久化优势在于特定情况下,操作比标记下传更加方便。其时间复杂度是稳定的,可能比标记下传略慢。