1. 程式人生 > >【解題報告】UVALive 3938 線段樹深入使用

【解題報告】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;
}