【解题报告】UVALive 3938 线段树深入使用

来源:互联网 发布:酷派手机自动开启数据 编辑:程序博客网 时间:2024/06/10 11:27

这道题说给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 <bits/stdc++.h>using namespace std#define maxn 500009#define lson n << 1#define rson n << 1 | 1typedef long long ll;typedef pair<ll, ll> 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;}

0 0
原创粉丝点击