区间最值操作与历史最值问题(二)

本文深入探讨了区间最值操作与历史最值问题,通过讲解多个题目,如[BZOJ 3064] CPU监控、[SPOJ1557 GSS2] Can you answer these queries II等,详细解析了如何使用线段树维护区间历史最大值和最小值。文章介绍了没有区间最值操作时的处理方法,以及在加入区间最值操作后的处理策略,包括同向和反向询问。最后,文章通过[UOJ 170] Picks loves segment tree VIII展示了更复杂的BOSS级问题的解决方案,涉及区间加、最大值和最小值的维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前一篇博客 中,我们详细介绍了区间取最值操作。接下来我们开始介绍区间历史最值问题!

区间历史最值

对于一个序列 A A A,它的历史最值指的是它从初始化到当前达到的最大值 / 最小值,我们称之为历史最大值和历史最小值。维护历史最大值和历史最小值是有一些套路的。

没有区间最值操作时

我们可以采用延用懒标记的做法。
我们要注意到,在线段树上打标记,标记是会有生命周期的。在标记下放之前,我们永远不会访问到子节点。因此,我们可以记录在这个点打的标记的当前值和历史最值,下传标记的时候对子节点的历史最值进行更新。
如果不理解的话也没有关系,我们可以结合例题进行理解!

[BZOJ 3064] CPU监控

给出长度为 n ( n ≤ 1 0 5 ) n(n\le 10^5) n(n105) 的序列 A A A,定义一个辅助数组 B B B,初始时与 A A A 完全相同。给出 m ( m ≤ 1 0 5 ) m(m\le 10^5) m(m105) 次操作,每次操作为以下四种操作之一:

  1. 给出 l , r , k l,r,k l,r,k,对于所有 i ∈ [ l , r ] i\in[l,r] i[l,r],将 A i A_i Ai 加上 k k k
  2. 给出 l , r , k l,r,k l,r,k,对于所有 i ∈ [ l , r ] i\in[l,r] i[l,r],将 A i A_i Ai 变成 k k k
  3. 给出 l , r l,r l,r,求序列 A i A_i Ai 的最大值
  4. 给出 l , r l,r l,r,求序列 B i B_i Bi 的最大值

每次操作后,对于所有 i i i,将 B i B_i Bi 变成 max ⁡ ( A i , B i ) \max(A_i,B_i) max(Ai,Bi)

序列 B B B 就是序列 A A A 的历史最大值,询问 4 4 4 就是询问 [ l , r ] [l,r] [l,r] A A A 的历史最大值的最大值。
我们先考虑线段树上的每个节点要维护一些什么信息。先考虑只有区间加操作。那么我们肯定需要维护的是区间当前最大值 m x mx mx,区间历史最大值 m x ′ mx' mx,还有这个节点当前加了多少值 a d d add add,以及这个节点在标记下放前加的值的历史最大值 a d d ′ add' add。那么标记下传时,我们考虑更新儿子节点的值和标记:
m x c h ′ = max ⁡ ( m x c h ′ , m x c h + a d d r t ′ ) a d d c h ′ = max ⁡ ( a d d c h ′ , a d d c h + a d d r t ) mx'_{ch}=\max(mx'_{ch},mx_{ch}+add'_{rt})\\ add'_{ch}=\max(add'_{ch},add_{ch}+add_{rt}) mxch=max(mxch,mxch+addrt)addch=max(addch,addch+addrt)

其实理解了打标记和标记下传的详细过程的话,这个还是很好理解的。
接下来考虑有覆盖操作。我们显然还要维护一个 c o v , c o v ′ cov,cov' cov,cov 分别表示当前覆盖的值和历史最大覆盖值。我们把标记看成二元组 ( a d d , c o v ) (add,cov) (add,cov),表示先加上 a d d add add 再把值变成 c o v cov cov。那么,考虑加法操作,当 c o v cov cov 不存在值时,我们直接在 a d d add add 上进行加减,否则我们在 c o v cov cov 上进行加减。根据常识直观理解,这个做法显然是正确的。
下面给出这题的代码。
我的代码真是优美呀


#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define lson l, m, rt << 1
#define rson m + 1, r, rt << 1 | 1
#define lc rt << 1
#define rc rt << 1 | 1
const int N = 1e5 + 5;
const LL inf = 1e18;
struct node{
   
	LL add, add_;
	LL cov, cov_;
	LL mx, mx_;
}d[N * 4];
void pushup(int rt){
   
	d[rt].mx = max(d[lc].mx, d[rc].mx);
	d[rt].mx_ = max(d[lc].mx_, d[rc].mx_);
}
void build(int l, int r, int rt){
   
	d[rt].cov = d[rt].cov_ = -inf;
	if(l == r){
   
		int x;
		cin >> x;
		d[rt].mx = d[rt].mx_ = x;
		return;
	}
	int m = l + r >> 1;
	build(lson);
	build(rson);
	pushup(rt);
}
void pushadd(int rt, LL k, LL k_){
   
	d[rt].mx_ = max(d[rt].mx_, d[rt].mx + k_); d[rt].mx += k;
	if(d[rt].cov != -inf) d[rt].cov_ = max(d[rt].cov_, d[rt].cov + k_), d[rt].cov += k;
	else d[rt].add_ = max(d[rt].add_, d[rt].add + k_), d[rt].add += k; 
}
void pushcov(int rt, LL k, LL k_){
   
	d[rt].mx_ = max(d[rt].mx_, k_); d[rt].mx = k;
	d[rt].cov_ = max(d[rt].cov_, k_); d[rt].cov = k;
}
void pushdown(int rt){
   
	pushadd(lc, d[rt].add, d[rt].add_);
	pushadd(rc, d[rt].add, d[rt].add_);
	d[rt].add = d[rt].add_ = 0;
	if(d[rt].cov != -inf) pushcov(lc, d[rt].cov, d[rt].cov_), pushcov(rc, d[rt].cov, d[rt].cov_), d[rt].cov = d[rt].cov_ = -inf;
}
void add(int l, int r, int rt, int a, int b, LL c){
   
	if(l >= a && r <= b){
   
		pushadd(rt, c, c);
		return;
	}
	pushdown(rt);
	int m = l + r >> 1;
	if(a <= m) add(lson, a, b, c);
	if(b > m) add(rson, a, b, c);
	pushup(rt);
}
void cov(int l, int r, int rt, int a, int b, LL c){
   
	if(l >= a && r <= b){
   
		pushcov(rt, c, c);
		return;
	}
	pushdown(rt);
	int m = l + r >> 1;
	if(a <= m) cov(lson, a, b, c);
	if(b > m) cov(rson, a, b, c);
	pushup(rt);
}
LL query1(int l, int r, int rt, int a, int b){
   
	if(l >= a && r <= b) return d[rt].mx;
	int m = l + r >> 1;
	pushdown(rt);
	LL ans = -1e18;
	if(a <= m) ans = max(ans, query1(lson, a, b));
	if(b > m) ans = max(ans, query1(rson, a, b));
	return ans;
}
LL query2(int l, int r, int rt, int a, int b){
   
	if(l >= a && r <= b) return d[rt].mx_;
	int m = l + r >> 1;
	pushdown(rt);
	LL ans = -1e18;
	if(a <= m) ans = max(ans, query2(lson, a, b));
	if(b > m) ans = max(ans, query2(rson, a, b));
	return ans;
}
int main(){
   
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	int n;
	cin >> n;
	build(1, n, 1);
	int m;
	cin >> m;
	for(int i = 1; i <= m; i++){
   
		char o[3];
		int x, y, z;
		cin >> o >> x >> y;
		if(o[0] == 'Q') cout << query1(1, n, 1, x, y) << '\n';
		if(o[0] 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值