245. 你能回答这些问题吗
给定长度为 N 的数列 A,以及 M 条指令,每条指令可能是以下两种之一:
2. 2 x y , 把 改成 。
对于每个查询指令,输出一个整数表示答案。
这道题要求的是 区间查询 和 单点修改,每一次查询都是求最大连续区间和,鉴于我是在《算法竞赛进阶指南》的线段树篇看到的这个题目,所以我们使用 线段树 来求解这个问题。线段树使我们能够动态维护区间信息,但是我们依然需要想办法来让各个区间的最大连续区间和可以合并起来。
我们使用正常的线段树建树方法来 Build 这个线段树,同时再维护4个信息:区间和 sum,区间最大连续子段和 val,从左端点开始的最大连续子段和 lsum,从右端点开始的最大连续子段和 rsum。
在节点p是叶节点时,它的 sum, val, lsum, rsum 都相等,否则这个节点p的最大连续子段(val)和是下面三个值中最大的:节点p的左子树的最大连续子段和,节点p的右子树的最大连续子段和,节点p的左子树的最大连续右子段和(rsum)+右子树的连续最大左子段和(lsum)。
在建立线段树时,我们需维护每个节点的区间和(sum),最大左子段和(lsum) 为这个节点的左子树的最大左子段和 或者 左子树的区间和(sum)和右子树的最大左子段和。最大右子段和(rsum)同理。
查询线段树时,将符合条件的节点拿出,以是否在查询区间中 同时 是否能增大部分和为依据取舍答案节点,最后获得最终答案。
#include
using namespace std;
#define INF 0x3F3F3F3F
#define MAXN 5+500000
int n, m;
int a[MAXN];
struct Mem {
int l, r;
int sum, lsum, rsum, val;
} mem[MAXN*4];
void UpdateSegtree (int p) {
mem[p].sum = mem[p*2].sum + mem[p*2+1].sum; //单纯区间和
mem[p].lsum = max(mem[p*2].lsum, (mem[p*2].sum+mem[p*2+1].lsum)); //与最左端连接的部分的最大连续子段和
mem[p].rsum = max((mem[p*2].rsum+mem[p*2+1].sum), mem[p*2+1].rsum); //与最右端连接的部分的最大连续子段和
mem[p].val = max(max(mem[p*2].val, mem[p*2+1].val), (mem[p*2].rsum+mem[p*2+1].lsum)); //最大连续子段和:仅来自于一边,或者来自于左右两边的连接
}
void BuildSegtree (int p, int l, int r) { //建立线段树
mem[p].l = l; mem[p].r = r;
if (mem[p].l == mem[p].r) {
mem[p].val = mem[p].sum = mem[p].rsum = mem[p].lsum = a[l];
return;
}
int mid = (l+r)/2;
BuildSegtree(p*2, l, mid);
BuildSegtree(p*2+1, mid+1, r);
UpdateSegtree(p);
}
Mem QuerySegtree (int p, int l, int r) {
if (l<=mem[p].l && mem[p].r<=r) { return mem[p]; } //查询到这一次层时,如果包含了一个区域,那么这个区域严格包含在答案区间中,是答案的一部分
Mem ans, tr, tl; //建立3个节点:ans为本层的答案节点,tr为右子树,tl为左子树
tr.lsum = tr.rsum = tr.sum = tr.val = tl.lsum = tl.rsum = tl.sum = tl.val = -INF;
ans.sum = 0;
ans.lsum = ans.rsum = -INF;
int mid = (mem[p].l+mem[p].r)/2;
if (l <= mid) { //如果左子树包含在查询区间内
tl = QuerySegtree(p*2, l, r);
ans.sum += tl.sum; //ans需要向上传递,所以区间和需要计算
}
if (mid < r) {
tr = QuerySegtree(p*2+1, l, r);
ans.sum += tr.sum;
}
ans.val = max(max(tr.val, tl.val), (tl.rsum+tr.lsum));
if (l <= mid) ans.lsum = max(tl.lsum, (tl.sum+tr.lsum)); //如果左子树包含在查询区间内,那么本层答案中的lsum就可以有两种选项
else if (l > mid) ans.lsum = max(ans.lsum, tr.lsum); //否则只能是右子树的左最大子段和
if (mid < r) ans.rsum = max(tr.rsum, (tr.sum+tl.rsum));
else if (mid >= r) ans.rsum = max(ans.rsum, tl.rsum);
return ans;
}
void PchangeSegtree (int p, int x, int v) { //单点修改
if (mem[p].l == mem[p].r) {
mem[p].val = mem[p].sum = mem[p].rsum = mem[p].lsum = v;
return;
}
int mid = (mem[p].l+mem[p].r)/2;
if (x <= mid) PchangeSegtree(p*2, x, v);
else PchangeSegtree(p*2+1, x, v);
UpdateSegtree(p); //注意维护性质
}
int main () {
while (scanf("%d %d", &n, &m) != EOF) {
for (int i=1; i<=n; i++) {
scanf("%d", &a[i]);
}
BuildSegtree(1, 1, n);
for (int i=0; i r) swap(l, r);
printf("%d\n", QuerySegtree(1, l, r).val);
}
else PchangeSegtree(1, l, r);
}
}
}