线段树——区间修改(Lazy-Tag)

poj3468 A Simple Problem with Integers传送门

注意ans可能超出int

懒惰标记:之所以称为懒惰标记,是因为我们在区间修改时,只修该结点的值,并在节点新增加一个标记,让子节点暂时处于不更新的状态,等我们用到的时候再更新,这样当我们在查询的时候,如果我们到了一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如果有,就要按照标记一次性修改其子节点的信息,并且给子节点都标上相同的标记,同时消掉节点p的标记。

区间更新举例说明:当我们要对区间[0,2]的叶子节点增加2,利用区间查询的方法从根节点开始找到了非叶子节点[0-2],把它的值设置为1+2 = 3,并且把它的延迟标记设置为2,更新完毕;当我们要查询区间[0,1]内的最小值时,查找到区间[0,2]时,发现它的标记不为0,并且还要向下搜索,因此要把标记向下传递,把节点[0-1]的值设置为2+2 = 4,标记设置为2,节点[2-2]的值设置为1+2 = 3,标记设置为2(其实叶子节点的标志是不起作用的,这里是为了操作的一致性),然后返回查询结果:[0-1]节点的值4;当我们再次更新区间[0,1](增加3)时,查询到节点[0-1],发现它的标记值为2,因此把它的标记值设置为2+3 = 5,节点的值设置为4+3 = 7;

其实当区间更新的区间左右值相等时([i,i]),就相当于单节点更新,单节点更新只是区间更新的特例。

#include <stdio.h>
#include <string.h>
#define MAXN 100010
#define lson l,mid,now<<1  
#define rson mid+1,r,now<<1|1
#define LL long long int  
LL f[MAXN<<2],a[MAXN],lazy[MAXN<<2];
void build(int l,int r,int now)
{
	 if(l==r)
	 {
	 	f[now]=a[l];
	 	return;
	 }
	 int mid=(l+r)>>1;
	 build(lson);
	 build(rson);
	 f[now]=f[now<<1]+f[now<<1|1];
	 return;
}
void PushDown(int ln,int rn,int now)
{
	if(lazy[now])//是否有使命 
	{
		//赋予左右儿子使命(因为可能多次被赋予使命,所以"+=") 
		lazy[now<<1]+=lazy[now];
		lazy[now<<1|1]+=lazy[now];
		//左右儿子更新值 
		f[now<<1]+=lazy[now]*ln;
		f[now<<1|1]+=lazy[now]*rn;
		lazy[now]=0;//使命已经完成,收回特权 
	}
}
//区间修改a[l...r]+x
void Update(int L,int R,int x,int l,int r,int now)
{
	if(L<=l&&R>=r)	
	{
		f[now]+=x*(r-l+1);
		lazy[now]+=x;
		return;
	}
	int mid=(l+r)>>1;
	PushDown(mid-l+1,r-mid,now);//传递使命 
	if(L<=mid) Update(L,R,x,lson);
	if(R>mid) Update(L,R,x,rson);
	f[now]=f[now<<1]+f[now<<1|1];
}
//区间查询
LL Query(int L,int R,int l,int r,int now)
{
	if(L<=l&&R>=r)	return f[now];
	int mid=(l+r)>>1;
	PushDown(mid-l+1,r-mid,now);//传递使命 
	LL ans=0;
	if(L<=mid) ans+=Query(L,R,lson);
	if(R>mid) ans+=Query(L,R,rson);
	return ans;	
}
int main()
{
	int i,n,m,x,y;
	LL num;
	char ch;
	while(scanf("%d %d",&n,&m)!=EOF)
	{
		memset(a,0,sizeof(a));
		memset(f,0,sizeof(f));
		memset(lazy,0,sizeof(lazy));
		for(i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		build(1,n,1);
		getchar();
		while(m--)
		{
			scanf("%c",&ch);
			if(ch=='Q')
			{
				scanf("%d %d",&x,&y);
				printf("%lld\n",Query(x,y,1,n,1));
			}
			else if(ch=='C')
			{
				scanf("%d %d %lld",&x,&y,&num);
				Update(x,y,num,1,n,1);
			}
			getchar();
		}
	}
	return 0;
}

 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值