poj 3468 线段树区间更新 lazy思想

题目大意:

      题目挺好理解的,就是给你一串数字,然后会在某一区间上,使区间的所有数字都加上一个数,也就是更新这段区间的数字,最后再进行对某一区间的求值操作。

题目分析:

      因为这道题目和我之前刚刚做的那道题目很类似,都是区间段更新的题目,但是有所不同的是,那个题目是直接赋值更新,而这个题目是区间进行加和,不是直接赋值,所以就有一点比较麻烦的是,是否需要对单个叶子节点进行更新,如果更新,无疑要耗费很多的时间,但是如果不更新,又难以处理数据,得到正确的求解。

     后来在网上看到了一个线段树的很好的思想,就是lazy思想,简单来说,就是首先更新父节点,当恰好属于父节点的时候,可以直接得出答案,否则再次向下更新子节点。分别有两个函数,一个是pushup(),是向上更新父节点。一个是pushdown(),是向下更新子节点。

       这个lazy思想主要是用在update里的。if(tree[rt].l == l && r == tree[rt].r) 这里就是用到Lazy思想的关键时刻 正如上面说提到的,这里首先更新该节点的sum[rt]值,然后更新该节点具体每个数值应该加多少即add[rt]的值,注意此时整个函数就运行完了,直接return,而不是还继续向子节点继续更新,这里就是Lazy思想,暂时不更新子节点的值。那么什么时候需要更新子节点的值呢?答案是在某部分update操作的时候需要用到那部分没有更新的节点的值的时候,这里可能有点绕口。这时就掉用PushDown()函数更新子节点的数值。

      接下来的查询就需要用到rt子节点的值了,由于我们用了Lazy操作,这段的数值还没有更新,因此我们需要调用PushDown函数去更新之,满足if(add[rt])就说明还没有更新。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
#define LL long long
#define lson l,mid,rt*2
#define rson mid+1,r,rt*2+1 
#define  maxn 100000+10
LL sum[maxn<<2];
LL add[maxn<<2];
void  pushup(int rt){
	 sum[rt]=sum[rt*2]+sum[rt*2+1];
}

void pushdown(int rt,int m){
	if(add[rt]){
		add[rt*2]+=add[rt];
		add[rt*2|1]+=add[rt];
		sum[rt*2]+=add[rt]*(m-m/2);
		sum[rt*2|1]+=add[rt]*(m/2);
		add[rt]=0;
	}
	
}
void build(int l,int r,int rt){
	add[rt]=0;
	if(l==r){
		scanf("%I64d",&sum[rt]);
		return ;
	}
	int mid=( l + r )/2;
	build(lson);
	build(rson);
	pushup(rt);
}
void update(int L,int R,int c,int l,int r,int rt){
	
	if( L<=l && r<=R ){
	    add[rt] += c;
		sum[rt] += ( r-l +1 )*(LL)c;
		return ;
	}
	pushdown(rt,r-l+1);
    int mid = ( l+r ) / 2;
    if(L<=mid)
      update(L,R,c,lson);
    if(R>mid)
      update(L,R,c,rson);
	pushup(rt);
}
LL query(int L,int R,int l,int r,int rt){
	if( L<=l && R>=r )
		return sum[rt];
	pushdown(rt,r-l+1);
	int mid=(l+r)/2;
	int rnt=0;
	if( L <= mid )  rnt+=query(L,R,lson);
	if( R > mid )   rnt+=query(L,R,rson); 
	return rnt;
} 

int main(){
	int N,Q,a,b,c;
	scanf("%d%d",&N,&Q);
	build(1,N,1);
	while(Q--){
		char s[5];
	    scanf("%s",s);
	    if(s[0]=='Q'){
	    	scanf("%d%d",&a,&b);
	    	printf("%I64d\n",query(a,b,1,N,1));
		}
		if(s[0]=='C'){
			scanf("%d%d%d",&a,&b,&c);
			update(a,b,c,1,N,1);
		}
	}
	return 0;
}

http://blog.csdn.net/acceptedxukai/article/details/6933446以上的有很多是学习这篇博客的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值