题目描述
给定长度为 N N N 的数列 A A A,以及 M M M条指令,每条指令可能是以下两种之一:
1 x y
,查询区间 [ x , y ] [x,y] [x,y] 中的最大连续子段和,即 m a x x ≤ l ≤ r ≤ y ∑ i = l r A [ i ] max_{x≤l≤r≤y}{\sum \limits^r_{i=l}A[i] } maxx≤l≤r≤yi=l∑rA[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
输入样例
5 3
1 2 -3 4 5
1 2 3
2 2 -1
1 3 2
输出样例
2
-1
题目分析
这道题是一个很经典的线段树问题,要求维护区间最大连续子段和。
我们考虑线段树中每个节点维护的信息:肯定有区间最大连续子段和。我们再考虑能否仅用子节点的这一个信息维护父节点:对于一个区间 [ l , r ] [l,r] [l,r],如果它的最大连续子段和都落在 [ l , m i d ] [l,mid] [l,mid] 或 [ m i d + 1 , r ] [mid+1,r] [mid+1,r] 中,可以直接转移;但是在两个子区间内都有分布就不好直接维护。容易发现,这种情况下,最大连续子段和等于左子区间的最大后缀和加上右子区间的最大前缀和。因此,线段树中在维护区间的最大前缀和与最大后缀和这两个信息。
再考虑如何求一个区间最大前缀和: m a x ( max( max(左子区间的最大前缀和,左子区间和+右子区间的最大前缀和 ) ) )。类似地,最大后缀和也可通过区间和与最大后缀和求出。而区间和显然可以在维护线段树时直接更新,不需引入其它信息。
这样,我们线段树上一共维护四个信息:区间和 s u m sum sum,最大前缀和 l m a x lmax lmax,最大后缀和 r m a x rmax rmax,最大连续子段和 t m a x tmax tmax。
代码:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 500010;
int n, m;
int a[N];
struct node{
int l, r;
int sum, lmax, rmax, tmax;
} tr[N * 4];
void pushup(node &u, node &l, node &r){
u.sum = l.sum + r.sum;
u.lmax = max(l.lmax, l.sum + r.lmax);
u.rmax = max(r.rmax, r.sum + l.rmax);
u.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, a[r], a[r], a[r], a[r]};
else{
tr[u] = {
l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
node query(int u, int l, int r){
//注意这里查询出的结果是一个节点
if (tr[u].l >= l && tr[u].r <= r) return tr[u];
else{
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r);
else if (l > mid) 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;
}
}
}
void modify(int u, int x, int d){
if (tr[u].l == x && tr[u].r == x) tr[u] = {
x, x, d, d, d, d};
else{
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, d);
else modify(u << 1 | 1, x, d);
pushup(u);
}
}
int main(){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++)
scanf("%d", &a[i]);
build(1, 1, n);
while (m --){
int k, x, y;
scanf("%d%d%d", &k, &x, &y);
if (k == 1){
if (x > y) swap(x, y);
printf("%d\n", query(1, x, y).tmax);
}
else modify(1, x, y);
}
return 0;
}