[灵魂拷问♂]系列
SP1043 GSS1 - Can you answer these queries I
- 题目:链接
大致题意:求区间最大子段和,不带修改。
题解:
对于每个线段树节点。维护以下几个值:
l, r:左右端点
sum:区间和
val:区间最大子段和
lv:一定包括区间左端点的最大子段和
rv:一定包括区间右端点的最大子段和
- lv转移:lv = max(左儿子的lv, 左儿子的sum + 右儿子的lv)
- rv转移:rv = max(右儿子的rv,右儿子的sum + 左儿子的rv)
- val转移:val = max(max(左儿子的val, 右儿子的val), 左儿子rv + 右儿子lv)
- 这几种转移很好理解,就是分类讨论。
- 线段树的分类讨论无非就是都在mid左边的,都在mid右边的,横跨mid的。
注意,查询的时候。我们不能写成以下版本。
if(l <= mid) res = max(res, ask(p << 1, l, r)); if(r > mid) res = max(res, ask(p << 1 | 1, l, r)); return res;
- 因为这样写表达的含义就是指答案只来自于左区间和右区间。例如区间和、区间覆盖。但是当答案有来自于(取出左右区间的信息然后进行操作后得到的新信息)时,就要分三类讨论转移。
- 所以这题的查询代码如下
T ask(int p, int l, int r) //注意这里的函数返回值是一个结构体,方便操作 { if(t[p].l >= l && t[p].r <= r) return t[p]; int mid = t[p].l + t[p].r >> 1; if(mid < l) return ask(p2, l, r); //答案都来自右儿子 else if(mid >= r) return ask(p1, l, r); //答案都来自左儿子 else //答案来自左儿子或右儿子或(左右儿子信息融合后的信息) { T t1 = ask(p1, l, r), t2 = ask(p2, l, r), now; now.sum = t1.sum + t2.sum; now.lv = max(t1.lv, t1.sum + t2.lv); now.rv = max(t2.rv, t2.sum + t1.rv); now.l = l, now.r = r; now.val = max(max(t1.val, t2.val), t1.rv + t2.lv); return now; } }
- 代码:
#include <iostream> #include <cstdio> #define N 50005 #define p1 (p << 1) #define p2 (p << 1 | 1) using namespace std; struct T {int l, r, val, lv, rv, sum;} t[N * 4]; int n, m; int read() { int x = 0, f = 1; char c = getchar(); while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();} while(c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();} return x *= f; } void up(int p) { t[p].sum = t[p1].sum + t[p2].sum; t[p].lv = max(t[p1].lv, t[p1].sum + t[p2].lv); t[p].rv = max(t[p2].rv, t[p2].sum + t[p1].rv); t[p].val = max(t[p1].rv + t[p2].lv, max(t[p1].val, t[p2].val)); } void build(int p, int l, int r) { t[p].l = l, t[p].r = r; if(l == r) { int v = read(); t[p].lv = t[p].rv = v; t[p].val = t[p].sum = v; return; } int mid = l + r >> 1; build(p1, l, mid), build(p2, mid + 1, r); up(p); } T ask(int p, int l, int r) { if(t[p].l >= l && t[p].r <= r) return t[p]; int mid = t[p].l + t[p].r >> 1; if(mid < l) return ask(p2, l, r); else if(mid >= r) return ask(p1, l, r); else { T t1 = ask(p1, l, r), t2 = ask(p2, l, r), now; now.sum = t1.sum + t2.sum; now.lv = max(t1.lv, t1.sum + t2.lv); now.rv = max(t2.rv, t2.sum + t1.rv); now.l = l, now.r = r; now.val = max(max(t1.val, t2.val), t1.rv + t2.lv); return now; } } int main() { cin >> n; build(1, 1, n); cin >> m; for(int i = 1; i <= m; i++) { int l = read(), r = read(); printf("%d\n", ask(1, l, r).val); } return 0; }