poj 3468 线段树(区间更新)模板题

题目链接  点击打开链接


以下为参考链接

点击打开链接

点击打开链接


线段树单点更新和区间更新的区别:

1.每个结点中多了一个add值,代表该结点以下的结点需要增加的值;

2.build函数中,如果在建树的过程中就赋值给num,那么在建完树之后不要忘记pushup,因为此时只是叶子结点有值,上面的值都为空;这个在区间更新中很常用,因为区间更新中如果输入一个值,然后更新一个值,这样会很麻烦,会耗费更多的时间;

3.update函数中,区间更新多了一个pushdown函数,并且更新sum和add值的判断条件是树中结点的l~r和要更新的区间的l~r相等,此时sum加的值是整个区间的长度*要更新的值,然后add值记录后面每个结点需要加上的值,即:c;

4.pushdown函数最后不要忘了将延时标记add清零;

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cstdio>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1  
using namespace std;
const int inf=1<<20;
const int M = 1e5+5;
long long sum[M<<2],add[M<<2];//重点 lazy标记:add[rt] 代表 rt以下的节点所要增加的值 
struct node{
	int l,r;
	int mid()
	{
		return (l+r)>>1;
	}
}tree[M<<2];
void pushup(int rt)
{
	sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
	tree[rt].l=l;
	tree[rt].r=r;
	add[rt]=0;//
	if(l==r)//leaf
	{
		scanf("%I64d",&sum[rt]);
		return;
	}
	int m=tree[rt].mid();
	build(lson);
	build(rson);
	pushup(rt);
}
void PushDown(int rt,int len)//区间rt的长度为len 
{
	if(add[rt])
	{
		add[rt<<1]+=add[rt];
		add[rt<<1|1]+=add[rt];//往下标记 
		
		sum[rt<<1]+=add[rt]*(len-(len/2));
		sum[rt<<1|1]+=add[rt]*(len/2);
		add[rt]=0;//更新后还原add[rt] 
	}
}
void update(int c,int l,int r,int rt) // 区间l,r中的每个元素增加c 
{
	if(tree[rt].l==l&&tree[rt].r==r) //lazy思想 
	{
		 add[rt]+=c;
		 sum[rt]+=c*(r-l+1);
		 return;//暂时不更新子节点的值
		 //在某部分update操作的时候需要用到那部分没有更新的节点的值的时候,
		 //这里可能有点绕口。这时就掉用PushDown()函数更新子节点的数值。 
	}
	if(tree[rt].l==tree[rt].r)
	return;
	
	
	PushDown(rt,tree[rt].r-tree[rt].l+1);// 要分解区间时(要用到子区间) 
	//更新子区间(把以前还没更新的更新了) 
	int m=tree[rt].mid();
	
	//更新查询要区间l,r所在的那一段  
	if(r<=m)//在该节点的左边 
	{
		update(c,l,r,rt<<1);
	}
	else if(l>m)
	{
		update(c,l,r,rt<<1|1);
	}
	else//横跨中点 继续分解 
	{
		update(c,l,m,rt<<1);
		update(c,m+1,r,rt<<1|1);
	}
	pushup(rt);
}
long long query(int l,int r,int rt)
{
	if(l==tree[rt].l&&r==tree[rt].r)//完全包含 
	{
		return sum[rt];
	}
	//要用到子区间
	PushDown(rt,tree[rt].r-tree[rt].l+1);
	int m=tree[rt].mid();
	long long res=0;
	//分解区间 
	if(r<=m) 
	{
		res+=query(l,r,rt<<1);
	 } 
	 else if(l>m)//所查询区间在右半段 
	 {
	 	res+=query(l,r,rt<<1|1);
	 }
	 else//l<m<r<tree[rt].r
	 {
	 	res+=query(l,m,rt<<1);
	 	res+=query(m+1,r,rt<<1|1);
	 }
	 return res;
}
int main()
{
	int n,q;
	cin>>n>>q;
	build(1,n,1);
	while(q--)
	{
		char c;
		cin>>c;
		if(c=='C')
		{
			int a,b,c;
			cin>>a>>b>>c;
			update(c,a,b,1);
		}
		if(c=='Q')
		{
			int l,r;
			cin>>l>>r;
			cout<<query(l,r,1)<<endl;
		} 
	}
	
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值