线段树(区间查询、修改)

这篇博客主要介绍了如何使用树状数组(也称作线段树)来解决区间修改和区间查询的问题。博主通过给出一个POJ-3468的实例,详细解释了树状数组的构建、查询和修改操作,并提供了完整的C++代码实现。文章强调了在处理区间问题时,pushdown和pushup操作的重要性,这对于理解树状数组的工作原理非常关键。
摘要由CSDN通过智能技术生成


原题地址POJ - 3468 

队长给出了一个序列,想让你帮队长干活,你需要处理如下两种情况。

"C a b c"表示给[a, b]区间中的值全部增加c (-10000 ≤ c ≤ 10000)。

"Q a b" 询问[a, b]区间中所有值的和。


Input

第一行包含两个整数N, Q。1 ≤ N,Q ≤ 100000.

第二行包含n个整数,表示初始的序列A (-1000000000 ≤ Ai ≤ 1000000000)。

接下来Q行询问,格式如题目描述。

Output

对于每一个Q开头的询问,你需要输出相应的答案,每个答案一行。

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

直接建树就完了,版子题。

下放:

void pushdown ( int rt ) { // pushdown 操作
              if ( tre [ rt ]. lazy ) {
                                        tre [ rt * 2 ]. lazy += tre [ rt ]. lazy ; // 把父节点 rt lazy 传给左右儿子
                                        tre [ rt * 2 + 1 ]. lazy += tre [ rt ]. lazy ;
                                        tre [ rt * 2 ]. sum += ( tre [ rt * 2 ]. r - tre [ rt * 2 ]. l + 1 ) * tre [ rt ]. lazy ; // sum 加 上的值就是区 间长度* 父节点 .lazy
                                        tre [ rt * 2 + 1 ]. sum += ( tre [ rt * 2 + 1 ]. r - tre [ rt * 2 + 1 ]. l + 1 ) * tre [ rt ]. lazy ;
                                        tre [ rt ]. lazy = 0 ; // 不要忘记清空 lazy
                                }
}

核心建树:

关键查询:

 修改:

void modify ( int rt , int l , int r , ll pos ) {
                if ( tre [ rt ]. l >= l && tre [ rt ]. r <= r ) { tre [ rt ]. sum += ( tre [ rt ]. r - tre [ rt ]. l + 1 ) * pos ; // 如果有完整区间,给这个区间加上lazy 标记,下面的路先不需要走
                      tre [ rt ]. lazy += pos ; // lazy += pos ,表示 lazy 是需要给以 rt 为根的所有子树加pos
                }
                else {
                                pushdown ( rt ); // 没有找到完整区间就一直 pushdown
                                int mid = tre [ rt ]. mid ();
                                if ( l <= mid ) modify ( rt * 2 , l , r , pos );
                                if ( r > mid ) modify ( rt * 2 + 1 , l , r , pos );
                                pushup ( rt ); // 这里记得需要 pushup ,利用回溯过程,更新父节点信息
                        }        
}
完整代码:
                
#include<iostream>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<stdio.h>
using namespace std;
const int N=100005;
int a[N];
struct node{
	int l,r;
	long long sum;//注意范围
	long long lazy;
	int mid(){
		return (l+r)/2;
	}
}tree[N*4];
void pushup(int rt){
	tree[rt].sum=tree[rt*2].sum+tree[rt*2+1].sum;
}
void pushdown(int rt){
	if(tree[rt].lazy){
		tree[rt*2].lazy+=tree[rt].lazy;
		tree[rt*2+1].lazy+=tree[rt].lazy;
		tree[rt*2].sum+=(tree[rt*2].r-tree[rt*2].l+1)*tree[rt].lazy;
		tree[rt*2+1].sum+=(tree[rt*2+1].r-tree[rt*2+1].l+1)*tree[rt].lazy;
		tree[rt].lazy=0;
	}
}
void build(int rt,int l,int r){
	if(l==r) tree[rt]={l,r,a[l],0};
	else {
		tree[rt]={l,r};
		int mid=tree[rt].mid();
		build(rt*2,l,mid);
		build(rt*2+1,mid+1,r);
		pushup(rt);
	}
}
long long query(int rt,int l,int r){
	if(tree[rt].l>=l&&tree[rt].r<=r){
		return tree[rt].sum;
	}
	else {
		pushdown(rt);
		int mid=tree[rt].mid();
		long long sum=0;
		if(l<=mid) sum+=query(rt*2,l,r);
		if(r>mid) sum+=query(rt*2+1,l,r);
		return sum;
	}
}
void modify(int rt,int l,int r,long long pos){
	if(tree[rt].l>=l&&tree[rt].r<=r){
		tree[rt].sum+=(tree[rt].r-tree[rt].l+1)*pos;
		tree[rt].lazy+=pos;
	}
	else{
		pushdown(rt);
		int mid=tree[rt].mid();
		if(l<=mid) modify(rt*2,l,r,pos);
		if(r>mid) modify(rt*2+1,l,r,pos);
		pushup(rt);
	}
}
  int main(){
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
  	int n,m;
  	cin>>n>>m;
  	for(int i=1;i<=n;i++){
  		cin>>a[i];
 	 }
 	 build(1,1,n);
 	 while(m--){
 	 	char op[5];
 	 	cin>>op;
 	 	int l,r;
 	 	cin>>l>>r;
 	 	long long pos;
 	 	if(op[0]=='Q') {
                   long long opp=query(1,l,r);
                   cout<<opp<<"\n";
                  }
 	 	else {
 	 	    cin>>pos;
 	 		modify(1,l,r,pos);
 		 }
 	 }
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值