https://www.acwing.com/problem/content/description/246/
给定长度为 N N N的数列 A A A,以及 M M M条指令,每条指令可能是以下两种之一:1 x y
,查询区间 [ x , y ] [x,y] [x,y]中的最大连续子段和,即 max x ≤ l ≤ r ≤ y ∑ i = l r A [ i ] \max_{x≤l≤r≤y}{∑_{i=l}^r A[i]} maxx≤l≤r≤y∑i=lrA[i];2 x y
,把 A [ x ] A[x] A[x]改成 y y y。对于每个查询指令,输出一个整数表示答案。
输入格式:
第一行两个整数 N , M N,M N,M。第二行 N N N个整数 A [ i ] A[i] A[i]。接下来 M M M行每行 3 3 3个整数 k , x , y k,x,y k,x,y, k = 1 k=1 k=1表示查询(此时如果 x > y x>y x>y,请交换 x , y x,y x,y), k = 2 k=2 k=2表示修改。
输出格式:
对于每个查询指令输出一个整数表示答案。每个答案占一行。
数据范围:
N ≤ 500000 , M ≤ 100000 N≤500000,M≤100000 N≤500000,M≤100000
− 1000 ≤ A [ i ] ≤ 1000 −1000≤A[i]≤1000 −1000≤A[i]≤1000
涉及到区间查询和单点修改,可以用线段树来做。考虑一下每个树节点要存什么,首先其维护的区间左右端点肯定是要的,接着,维护的区间的最大子段和肯定也是要的;如果需要计算当前区间的最大子段和,其有若干种情况,一种是该最大子段和完全包含在左右子区间中,另一种是最大子段和要横跨中点;前者只需要子区间的最大子段和即可,后者则需要左子区间的最大后缀和与右子区间的最大前缀和,这样最大子段和已经解决了。接下来的问题是,要求当前区间的最大前后缀,是否可以依靠左右子区间的相关信息求出来。这一点也可以类似讨论,当前区间的最大前缀可能是左子区间的最大前缀(未横跨中点),也可能是左子区间的和加上右子区间的最大前缀;当前区间的最大后缀类似。所以我们看出,还需要存一下维护的区间和。综上所述,我们需要在树节点里存储的信息就是:当前维护区间的左右端点,维护区间的最大前后缀和,维护区间的区间和还有维护区间的最大子段和。代码如下:
#include
using namespace std;
const int N = 500010;
int n, m;
// w数组存输入数列,从w[1]开始存
int w[N];
struct Node {
int l, r;
int sum, lmax, rmax, tmax;
} tr[4 * N];
// 已知左右子区间的树节点l和r,更新当前区间的节点root
void pushup(Node &root, Node &l, Node &r) {
root.sum = l.sum + r.sum;
root.lmax = max(l.lmax, l.sum + r.lmax);
root.rmax = max(r.rmax, r.sum + l.rmax);
// 最大子段和要么完全在左右子区间内部,要么横跨中点
root.tmax = max(max(l.tmax, r.tmax), l.rmax + r.lmax);
}
void pushup(int u) {
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r) {
if (l == r) tr[u] = {l, r, w[l], w[l], w[l], w[l]};
else {
// 注意上面的pushup里没有更新tr[u]维护的区间左右端点,所以这里要加上去
tr[u] = {l, r};
int m = l + (r - l >> 1);
build(u << 1, l, m), build(u << 1 | 1, m + 1, r);
pushup(u);
}
}
int modify(int u, int x, int v) {
if (tr[u].l == x && tr[u].r == x) tr[u] = {x, x, v, v, v, v};
else {
int m = tr[u].l + (tr[u].r - tr[u].l >> 1);
if (x <= m) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
}
Node query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u];
int m = tr[u].l + (tr[u].r - tr[u].l >> 1);
if (r <= m) return query(u << 1, l, r);
else if (l > m) return query(u << 1 | 1, l, r);
else {
Node left = query(u << 1, l, r), right = query(u << 1 | 1, l, r);
Node res;
pushup(res, left, right);
return res;
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> w[i];
build(1, 1, n);
while (m--) {
int k, x, y;
cin >> k >> x >> y;
if (k == 1) {
if (x > y) swap(x, y);
cout << query(1, x, y).tmax << endl;
} else modify(1, x, y);
}
return 0;
}
预处理时间复杂度 O ( N ) O(N) O(N),每次操作时间 O ( log N ) O(\log N) O(logN),空间 O ( N ) O(N) O(N)。