这道题说给n个数,每一次给一段区间求区间最大子段和。简单分析吧,求区间最大子段和一般使用dp,但是这题的查询最多可以有50W个,每次都重新算必定超时。那么就想着要把数据存下来,那么存什么呢?传统的线段树每一个节点会存当前区间我们需要的值,比如max值sum值等,但是在这一题中,我们需要的是求最大子段和的值,但是我们没有必要把他存在节点中。为什么呢?因为查询区间极大概率不能使用一个节点来表示,那么当我需要把两个节点合并求区间最大子段和时,存的最大子段和就失去了其意义。因为不能保证两段节点最大子段和区间是相接的。
因此,在这道题中,我们使用一个sum数组,表示到i为止所有数的和,那么每一段区间的子段和就可以用sum[r]-sum[l-1]来表示。那么还有另一个问题,每一个节点应该存什么呢?每一个节点应该存取该节点所代表区间的最大子段和区间,前缀区间的末尾和后缀区间的开始。为什么要存这些东西呢?一段区间的最大子段和可能是左子树的最大子段和,右子树的最大子段和,或者左子树的后缀加上右子树的前缀。明白了需要存什么,剩下的问题就比较好解决了。
最后,在构建这颗线段树的时候,有什么需要注意的呢?在回溯构造父节点,进行区间合并的时候,需要注意,并不是“如果(左子树的最大前缀和长度==左子树的长度 && 右子树的前缀和>0)就进行合并”,以构建父节点的前缀和为例,正确的式子为:
if(左子树的区间和+右子树的前缀和>左子树的前缀和)
父节点的前缀结尾=右子树的前缀结尾的点
else
父节点的前缀结尾=左子树的前缀结尾点
比如说,左子树为 5 2 -3 而右子树为 8 4 -3,那么按照错误的式子,应该是不进行合并的。但是我们可以发现,左子树的和为4加上右子树的前缀和为4+12=16>7,所以应当进行合并。
#include
using namespace std
#define maxn 500009
#define lson n << 1
#define rson n << 1 | 1
typedef long long ll;
typedef pair PAIR;
struct node {
ll l, r, pl, pr;//pl前趋的终止,pr后缀的开始
PAIR sub;
}seg[maxn << 2];
ll box[maxn], sum[maxn];
ll csum(ll l, ll r) { return sum[r] - sum[l - 1]; }
ll csum(PAIR n) { return sum[n.second] - sum[n.first - 1]; }
PAIR cmax(PAIR a, PAIR b)
{
if (csum(a) != csum(b))return csum(a) > csum(b) ? a : b;
return a < b ? a : b;
}
void build(ll n, ll l, ll r)
{
seg[n].l = l;seg[n].r = r;
if (l == r)
{
sum[l] = box[l] + sum[l - 1];
seg[n].pl = seg[n].pr = l;
seg[n].sub = make_pair(l, r);
return;
}
ll mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
seg[n].pl = csum(l, seg[rson].pl) > csum(l, seg[lson].pl) ? seg[rson].pl : seg[lson].pl;
seg[n].pr = csum(seg[lson].pr, r) >= csum(seg[rson].pr, r) ? seg[lson].pr : seg[rson].pr;
seg[n].sub = cmax(seg[lson].sub, cmax(seg[rson].sub, make_pair(seg[lson].pr, seg[rson].pl)));
}
PAIR pre_query(ll n, ll l, ll r)
{
if (seg[n].pl <= r)return make_pair(seg[n].l, seg[n].pl);
ll mid = (seg[n].l + seg[n].r) >> 1;
if (r <= mid)return pre_query(lson, l, r);
PAIR p1 = pre_query(rson, l, r);
p1.first = seg[n].l;
return cmax(p1, make_pair(seg[n].l, seg[lson].pl));
}
PAIR suf_query(ll n, ll l, ll r)
{
if (seg[n].pr >= l)return make_pair(seg[n].pr, seg[n].r);
ll mid = (seg[n].l + seg[n].r) >> 1;
if (l > mid)return suf_query(rson, l, r);
PAIR p1 = suf_query(lson, l, r);
p1.second = seg[n].r;
return cmax(p1, make_pair(seg[rson].pr, seg[n].r));
}
PAIR query(ll n, ll l, ll r)
{
if (l <= seg[n].l && seg[n].r <= r)
return seg[n].sub;
ll mid = (seg[n].l + seg[n].r) >> 1;
if (l > mid)return query(rson, l, r);
if (r <= mid)return query(lson, l, r);
PAIR p2 = pre_query(rson, l, r);
PAIR p3 = suf_query(lson, l, r);
PAIR p1 = cmax(query(lson, l, r), query(rson, l, r));
return cmax(p1, make_pair(p3.first, p2.second));
}
int main()
{
ll n, m, Case = 1, ql, qr;
while (cin >> n >> m)
{
printf("Case %lld:\n", Case++);
memset(seg, 0, sizeof(seg));
memset(sum, 0, sizeof(sum));
for (ll i = 1;i <= n;i++)
scanf_s("%lld", &box[i]);
build(1, 1, n);
while (m--)
{
scanf_s("%lld%lld", &ql, &qr);
PAIR x = query(1, ql, qr);
printf("%lld %lld\n", x.first, x.second);
}
}
return 0;
}