SPOJ GSS1 静态区间最大子段和
题目大意
给定一个序列,支持区间最大子段和,\(n \leq 50000\)。
思路
一个序列的最大字段和可能出现在左半侧最大子段和,右半侧最大子段和和跨越两边的最大子段和。
维护四个标记:mxsum,mxpre,mxsuf,sum
,线段树操作即可。
有一个细节,查询的时候不能直接用左右孩子的最大后缀/前缀,因为查询的区间可能小于最大子段和出现的区间。
两种解决办法:
- 记录最大子段和出现的位置,维护的时候判断边界
- 将尽可能多的信息传递上来,用于计算。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
struct node {
int l,r;
int mxsum;
int mxsuf;
int mxpre;
int sum;
};
int a[N];
struct segment_tree {
node t[N << 2];
void update(int rt) {
int ch = rt << 1;
t[rt].sum = t[ch].sum + t[ch + 1].sum;
t[rt].mxsum = max(max(t[ch].mxsum,t[ch + 1].mxsum),t[ch].mxsuf + t[ch + 1].mxpre);
t[rt].mxpre = max(t[ch].mxpre,t[ch].sum + t[ch + 1].mxpre);
t[rt].mxsuf = max(t[ch + 1].mxsuf,t[ch + 1].sum + t[ch].mxsuf);
}
void build(int l,int r,int rt) {
t[rt].l = l;
t[rt].r = r;
if(l == r) {
t[rt].sum = t[rt].mxsuf = t[rt].mxpre = t[rt].mxsum = a[l];
return;
}
int mid = (l + r) >> 1;
int ch = rt << 1;
build(l,mid,ch);
build(mid + 1,r,ch + 1);
update(rt);
}
node query (int l,int r,int rt) {
if(l == t[rt].l and r == t[rt].r) {
return t[rt];
}
int mid = (t[rt].l + t[rt].r) >> 1;
int ch = rt << 1;
if(r <= mid) {
return query(l,r,ch);
}
else if(l > mid) {
return query(l,r,ch + 1);
}else {
node ret1 = query(l,mid,ch);
node ret2 = query(mid + 1,r,ch + 1);
node res;
res.sum = ret1.sum + ret2.sum;
res.mxsum = max(max(ret1.mxsum,ret2.mxsum),ret1.mxsuf + ret2.mxpre);
res.mxpre = max(ret1.mxpre,ret1.sum + ret2.mxpre);
res.mxsuf = max(ret2.mxsuf,ret2.sum + ret1.mxsuf);
return res;
}
}
} ST;
int n,q;
int main () {
cin >> n;
for(int i = 1;i <= n; i ++) {
cin >> a[i];
}
ST.build(1,n,1);
cin >> q;
while(q --) {
int l,r;
cin >> l >> r;
cout << ST.query(l,r,1).mxsum << endl;
}
return 0;
}