线段树区间更新模板——懒标记(lazy-tag)的运用

一、定义

  • 在线段树中会遇到区间更新的情况,例如 在区间求和问题中,令[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; 
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章