Loading

P4314 CPU 监控 (线段树维护历史最大值)

P4314 CPU 监控

这题是维护历史最大值模板。

先套线段树,考虑怎么维护标记。

我们发现普通的标记的维护遵循能合并就合并,但是这就会出现问题:假如一个标记还没有下传时就被修改(也就是减小),那就会导致子树的历史最大值不正确(变小)。

考虑先不合并同一个节点的标记,把它们看成一个操作序列。这里讲一个简化的问题:

  1. 支持区间加
  2. 查询当前最大值
  3. 查询历史最大值

考虑怎么下传标记。显然每个标记都下传更新一次是可行的,但是我们无法维护这样一个操作序列。

设操作序列的前缀和为 \(S[1\cdots i]\),那么只需要下传 \(\max(S[1\cdots i])\) 即可维护答案。考虑合并两个标记,设 \(S_1[1\cdots n]\)\(S_2[1\cdots m]\),合并完为 \(S_3[1\cdots n+m]\)

容易推出 \(\max (S_3) = (\max (S_1), S_1[n]+\max (S_2))\)

此时 \(S_1[n]\) 为我们普通加法标记的值。

具体的说,我们只需要维护历史最大加和当前加两个标记即可维护。

回到这题,多了操作区间赋值,若操作序列中有两种标记,不好维护。容易发现,若前面出现过赋值操作,那么之后的加法标记都可以直接加到赋值标记中变为赋值标记

此时操作序列转化为,一段加法序列+一段赋值序列两段。前面的加法序列用前面说的一样维护,设后面赋值序列为 \(C\),后面的赋值序列的历史最大赋值即为 \(\max C\)

pushdown 时分两段下传即可。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second

typedef long long i64;
const int N = 100010;
int n, m;
int a[100010];
i64 inf = 0x3f3f3f3f3f3f3f3f;
struct seg {
	i64 max1, max2;
	i64 fg, add;
	i64 vis, hfg, hadd; //fg 覆盖,add 加法
} t[N << 2];
void pushup(int u) {
	t[u].max1 = std::max(t[u << 1].max1, t[u << 1 | 1].max1);
	t[u].max2 = std::max(t[u << 1].max2, t[u << 1 | 1].max2);
}
void build(int u, int l, int r) {
	if(l == r) {
		t[u].max1 = t[u].max2 = a[l];
		return;	
	}
	int mid = (l + r) >> 1;
	build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
	pushup(u);
}
void add(int u, i64 add, i64 hadd) {
	if(t[u].vis) {
		t[u].hfg = std::max(t[u].hfg, t[u].fg + hadd);
		t[u].max2 = std::max(t[u].max2, t[u].max1 + hadd);
		t[u].max1 += add;
		t[u].fg += add;
	}
	else {
		t[u].hadd = std::max(t[u].hadd, t[u].add + hadd);
		t[u].max2 = std::max(t[u].max2, t[u].max1 + hadd);
		t[u].max1 += add;
		t[u].add += add;
	}
}
void cov(int u, i64 fg, i64 hfg) {
	if(t[u].vis) {
		t[u].hfg = std::max(t[u].hfg, hfg);
		t[u].max2 = std::max(t[u].max2, hfg);
	}
	else {
		t[u].vis = 1;
		t[u].hfg = hfg;
		t[u].max2 = std::max(t[u].max2, hfg);
	}
	t[u].fg = t[u].max1 = fg;
}
void pushdown(int u) {
	add(u << 1, t[u].add, t[u].hadd);
	add(u << 1 | 1, t[u].add, t[u].hadd);
	t[u].add = t[u].hadd = 0;
	if(t[u].vis) {
		cov(u << 1, t[u].fg, t[u].hfg);
		cov(u << 1 | 1, t[u].fg, t[u].hfg);
		t[u].fg = t[u].hfg = t[u].vis = 0;
	}
}
void update(int u, int l, int r, int L, int R, i64 x, int y) {
	if(L <= l && r <= R) {
		if(!y) add(u, x, x);
		else cov(u, x, x);
		return;
	}
	int mid = (l + r) >> 1;
	pushdown(u);

	if(L <= mid) update(u << 1, l, mid, L, R, x, y);
	if(R > mid) update(u << 1 | 1, mid + 1, r, L, R, x, y);

	pushup(u);
}
i64 qmax1(int u, int l, int r, int L, int R) {
	if(L <= l && r <= R) {
		return t[u].max1;
	}
	int mid = (l + r) >> 1;
	pushdown(u);

	if(R <= mid) return qmax1(u << 1, l, mid, L, R);
	if(L > mid) return qmax1(u << 1 | 1, mid + 1, r, L, R);
	return std::max(qmax1(u << 1, l, mid, L, R), qmax1(u << 1 | 1, mid + 1, r, L, R)); 
}
i64 qmax2(int u, int l, int r, int L, int R) {
	if(L <= l && r <= R) {
		return t[u].max2;
	}
	int mid = (l + r) >> 1;
	pushdown(u);
	
	if(R <= mid) return qmax2(u << 1, l, mid, L, R);
	if(L > mid) return qmax2(u << 1 | 1, mid + 1, r, L, R);
	return std::max(qmax2(u << 1, l, mid, L, R), qmax2(u << 1 | 1, mid + 1, r, L, R)); 
}
void Solve() {
	std::cin >> n;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i];
	}
	build(1, 1, n);

	std::cin >> m;
	while(m--) {
		char c;
		std::cin >> c;
		int l, r;
		std::cin >> l >> r;
		if(c == 'Q') {
			std::cout << qmax1(1, 1, n, l, r) << "\n";
		} else if(c == 'A') {
			std::cout << qmax2(1, 1, n, l, r) << "\n";
		} else if(c == 'P') {
			int x;
			std::cin >> x;
			update(1, 1, n, l, r, x, 0);
		} else if(c == 'C') {
			int x;
			std::cin >> x;
			update(1, 1, n, l, r, x, 1);
		}
	}
}
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	Solve();

	return 0;
}
posted @ 2024-03-23 20:51  Fire_Raku  阅读(15)  评论(0编辑  收藏  举报