【线段树】区间修改区间最大

本文解析了一道经典的线段树模板题,涉及区间修改与查询区间最大值的操作。通过详细解释代码实现,帮助读者理解线段树数据结构及其在解决区间问题上的应用。

 

线段树模板题(三)——区间修改与查询区间最大

【题目描述】  

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间的最大值

 

 

【输入】

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内的最大值

【输出】

输出包含若干行整数,即为所有操作2的结果。

【样例输入】

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4                

【样例输出】

5
6
8

 

代码:

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define LD (o<<1)
#define RD (o<<1|1)
#define INF 0x7fffffff
struct Node{
	int l,r;
	int maxv,add;
};
struct Tree{
	Node t[N<<2];
	int a[N];
	inline void pushup(Node &o,Node &ld,Node &rd){
		o.maxv=max(ld.maxv,rd.maxv);
	}
	inline void pushdown(Node &o,Node &ld,Node &rd){
        ld.add+=o.add;ld.maxv+=o.add;
        rd.add+=o.add;rd.maxv+=o.add;
        o.add=0;
	}
	void build(int o,int l,int r){
		t[o].l=l;t[o].r=r;t[o].add=0;
		if(l==r){t[o].maxv=a[l];return;}
		int mid=(l+r)>>1;
		build(LD,l,mid);
		build(RD,mid+1,r);
		pushup(t[o],t[LD],t[RD]);
	}
	void update(int o,int l,int r,int v){
		if(l<=t[o].l&&t[o].r<=r){t[o].add+=v;t[o].maxv+=v;return;}
		pushdown(t[o],t[LD],t[RD]);
		int mid=(t[o].l+t[o].r)>>1;
		if(mid>=l)update(LD,l,r,v);
		if(mid<r)update(RD,l,r,v);
		pushup(t[o],t[LD],t[RD]);
	}
	int query(int o,int l,int r){
		if(l<=t[o].l&&t[o].r<=r)return t[o].maxv;
		pushdown(t[o],t[LD],t[RD]);
		int ans=0,mid=(t[o].l+t[o].r)>>1;
		if(mid>=l)ans=max(ans,query(LD,l,r));
		if(mid<r)ans=max(ans,query(RD,l,r));
		pushup(t[o],t[LD],t[RD]);
		return ans;
	}
}A;
int n,m;
inline int read(){
    int data=0,w=1;char ch=0;
    while(ch!='-'&&(!isdigit(ch)))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch)){
        data=(data<<3)+(data<<1)+(ch^'0');
        ch=getchar();
    }
    return data*w;
}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)A.a[i]=read();
	A.build(1,1,n);
	for(int i=1;i<=m;i++){
		int t=read(),x=read(),y=read();
		if(t==1){int v=read();A.update(1,x,y,v);}
		if(t==2)printf("%d\n",A.query(1,x,y));
	}
	return 0;
}

 

### 线段树区间修改的实现方法 线段树是一种高效的数据结构,用于处理动态区间查询修改操作。对于区间修改的操作,通常会引入懒惰传播(Lazy Propagation)机制来优化性能。 #### 基本概念 在支持区间修改的情况下,线段树通过维护一个额外的`lazy[]`数组记录尚未传递给子节点的延迟更新信息。这种设计可以减少不必要的递归调用次数,从而提高效率[^1]。 #### 关键函数说明 以下是实现线段树区间修改的核心部分: 1. **构建线段树** 构建过程与普通的线段树相同,初始化时需确保`lazy[]`数组全部置零。 2. **Push Down 函数** 当访问某个节点并发现该节点存在未解决的延迟标记时,需要将其影响向下传递至子节点,并清除当前节点上的标记。 ```cpp void pushDown(int k, int l, int r) { if (lazy[k]) { // 如果有延迟标记 int mid = (l + r) / 2; add(k * 2, l, mid, lazy[k]); // 更新左孩子 add(k * 2 + 1, mid + 1, r, lazy[k]); // 更新右孩子 lazy[k] = 0; // 清除当前节点的延迟标记 } } ``` 3. **Add 函数** `add()`负责执行具体的区间修改逻辑。如果目标区间完全覆盖当前节点,则直接应用修改;否则继续分解到子节点上。 ```cpp void add(int k, int l, int r, int ql, int qr, int val) { if (ql <= l && r <= qr) { // 完全覆盖的情况 s[k] += (r - l + 1) * val; // 修改当前区间的总和 lazy[k] += val; // 设置延迟标记 return; } int mid = (l + r) / 2; pushDown(k, l, r); // 下推延迟标记 if (ql <= mid) add(k * 2, l, mid, ql, qr, val); if (qr > mid) add(k * 2 + 1, mid + 1, r, ql, qr, val); s[k] = s[k * 2] + s[k * 2 + 1]; // 合并左右孩子的结果 } ``` 4. **Query 函数** 查询过程中也需要注意是否存在延迟标记,若有则先进行下推操作再继续查找。 ```cpp long long query(int k, int l, int r, int ql, int qr) { if (ql <= l && r <= qr) { // 完全覆盖的情况 return s[k]; } int mid = (l + r) / 2; pushDown(k, l, r); // 下推延迟标记 long long res = 0; if (ql <= mid) res += query(k * 2, l, mid, ql, qr); if (qr > mid) res += query(k * 2 + 1, mid + 1, r, ql, qr); return res; } ``` 以上即为完整的线段树区间修改实现方案[^2][^3]。 ### 时间复杂度分析 每次修改查询操作最多涉及从根节点到叶子节点的一条路径上的所有节点,因此时间复杂度均为\( O(\log N) \)[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值