【解題報告】UVALive 3938 線段樹深入使用
這道題說給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 | 1
typedef 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;
}