一、定义
-
在线段树中会遇到区间更新的情况,例如 在区间求和问题中,令[a,b]区间内的值全部加c,若此时再采用单点更新的方法,就会耗费大量时间,这个时候就要用到懒标记来进行区间更新了。
-
懒标记(lazy-tag),又叫做延迟标记,举例说明。
设 当前结点对应区间[l, r],待更新区间[a, b]
当 a ≤ l ≤ r ≤ b,即 [l, r]∈[a,b]时,不再向下更新,仅更新当前结点,并在该结点加上懒标记,当必须得更新/查询该结点的左右子结点时,再利用懒标记的记录向下更新(pushdown)——懒标记也要向下传递,然后移除该结点的懒标记。
这样就不用每次都更新到叶子结点,减少了大量非必要操作,从而优化时间复杂度。
二、模板
以下均以在区间求和问题中,令[a,b]区间内的值全部加c为例
laz[rt]:表示其该结点rt对应区间[l, r]内的所有数据均要加上laz[rt]
1. 建树
建树操作与无懒标记的线段树无异,但要记得对懒标记数组进行初始化。
int a[maxn],t[4*maxn],laz[4*maxn];
void build(int rt,int l,int r)
{
if(l==r)
{
t[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
t[rt]=t[rt<<1]+t[rt<<1|1];
}
2.向下更新
void pushdown(int rt,int l,int r) //对结点rt向下更新一层
{
if(laz[rt]==0) //laz[rt]==0直接返回
return;
//向下更新
//令左右子结点加上 laz[rt]*区间的数量
//令左右子结点的懒标记加上 laz[rt]
int mid=(l+r)>>1;
t[rt<<1]+=laz[rt]*(mid-l+1);
laz[rt<<1]+=laz[rt];
t[rt<<1|1]+=laz[rt]*(r-mid);
laz[rt<<1|1]+=laz[rt];
laz[rt]=0; //懒标记置零
}
3.区间更新
void updata(int rt,int l,int r,int a,int b,int c)
{
if(a<=l&&r<=b) //当前区间[l,r]∈待更新区间[a,b]
{
t[rt]+=c*(r-l+1); //更新当前结点,并加上懒标记
laz[rt]+=c;
return; //返回
}
pushdown(rt,l,r); //因为还要向下一层遍历,所有要先更新下一层
int mid=(l+r)>>1;
if(b<=mid) //待更新区间在中点(包括中点)左侧 —— 向左遍历
updata(rt<<1,l,mid,a,b,c);
else if(a>mid) //待更新区间在中点(不包括中点)右侧 —— 向右遍历
updata(rt<<1|1,mid+1,r,a,b,c);
else //否则
{
updata(rt<<1,l,mid,a,b,c);
updata(rt<<1|1,mid+1,r,a,b,c);
}
t[rt]=t[rt<<1]+t[rt<<1|1]; //不要漏了这条
}
4. 区间查询
带懒标记的查询操作,就是当查询时还需向下遍历的时候,要先把此处懒标记向下更新。
int query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr) //当前区间[l,r]∈待查询区间[ql,qr]
return t[rt];
if(r<ql||l>qr) //区间无重合
return 0;
//区间部分重合
pushdown(rt,l,r); //先向下更新
int mid=(l+r)>>1;
return query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
}
三、模板例题
注意结果可能会超出int,要用long long存储
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e5+7;
int N,Q;
LL a[maxn],t[4*maxn],laz[4*maxn];
void build(int rt,int l,int r)
{
if(l==r)
{
t[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
t[rt]=t[rt<<1]+t[rt<<1|1];
}
void pushdown(int rt,int l,int r)
{
if(laz[rt]==0)
return;
int mid=(l+r)>>1;
t[rt<<1]+=laz[rt]*(mid-l+1);
laz[rt<<1]+=laz[rt];
t[rt<<1|1]+=laz[rt]*(r-mid);
laz[rt<<1|1]+=laz[rt];
laz[rt]=0;
}
void updata(int rt,int l,int r,int a,int b,LL c)
{
if(a<=l&&r<=b)
{
t[rt]+=c*(r-l+1);
laz[rt]+=c;
return;
}
pushdown(rt,l,r);
int mid=(l+r)>>1;
if(b<=mid)
updata(rt<<1,l,mid,a,b,c);
else if(a>mid)
updata(rt<<1|1,mid+1,r,a,b,c);
else
{
updata(rt<<1,l,mid,a,b,c);
updata(rt<<1|1,mid+1,r,a,b,c);
}
t[rt]=t[rt<<1]+t[rt<<1|1];
}
LL query(int rt,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)
return t[rt];
if(r<ql||l>qr)
return 0;
pushdown(rt,l,r);
int mid=(l+r)>>1;
return query(rt<<1,l,mid,ql,qr)+query(rt<<1|1,mid+1,r,ql,qr);
}
int main()
{
scanf("%d %d",&N,&Q);
for(int i=1;i<=N;i++)
scanf("%lld",&a[i]);
build(1,1,N);
while(Q--)
{
getchar();
char C=getchar();
int a,b;
scanf("%d %d",&a,&b);
if(C=='C')
{
LL c;
scanf("%lld",&c);
updata(1,1,N,a,b,c);
}
else
printf("%lld\n",query(1,1,N,a,b));
}
return 0;
}