这道题是经典的“区间修改 + 区间查询”变种,我当年在考场上从暴力写到正解,拿了满分。今天把 4 种不同分段的写法全部公开。
题面简述:
给定长度为 n 的数组 a,执行 m 次操作:
1. 1 l r x:将 [l, r] 内每个数加上 x。
2. 2 l r:查询 [l, r] 内的最大值。
做法 1:暴力模拟(30 分)
每次操作遍历 l 到 r,修改或查询。复杂度 O(n*m)。
做法 2:差分 + 前缀和(60 分,仅针对静态查询)
如果只有一次全局查询,可以用差分。但这里是动态修改,所以仅适用于特殊数据。
做法 3:树状数组维护区间加 + 区间最值(85 分)
树状数组通常维护前缀和,但配合差分数组可以支持区间加和区间求和。对于最大值,虽然不如线段树灵活,但可以通过维护两个树状数组实现 O(log^2 n),适合卡常选手。
做法 4:线段树懒标记(100 分)
正解!维护 maxv 和 lazy 标记。
void pushup(int p) { maxv[p] = max(maxv[p<<1], maxv[p<<1|1]); }
void pushdown(int p) {
if (lazy[p]) {
maxv[p<<1] += lazy[p];
lazy[p<<1] += lazy[p];
maxv[p<<1|1] += lazy[p];
lazy[p<<1|1] += lazy[p];
lazy[p] = 0;
}
}
void update(int p, int l, int r, int ql, int qr, int x) {
if (ql <= l && r <= qr) {
maxv[p] += x;
lazy[p] += x;
return;
}
pushdown(p);
int mid = (l + r) >> 1;
if (ql <= mid) update(p<<1, l, mid, ql, qr, x);
if (qr > mid) update(p<<1|1, mid+1, r, ql, qr, x);
pushup(p);
}
复杂度稳定 O(m log n),完美通过。
考场经验:如果时间不够,先写暴力保底,再逐步优化到线段树。不要一上来就写复杂的树套树,CSP 的 T3 一般线段树就够用了。