【codevs 3981】動態最大子段和 / 線段樹
阿新 • • 發佈:2019-02-14
題
給定一個長度為 的序列 ,以及 次詢問,每次詢問給定 兩引數。
對於每次詢問,求 到 之間的最大子段和,子段的意思是連續非空子區間。
更形式化地解釋:對於每次詢問給定的 ,
求一個整數 ,使得存在整數 ,滿足 且 。
其中, , 在 範圍內但不保證答案在 範圍內。
解
看題就知道要資料結構啦,
我們直接考慮合併,對於某個區間,它的兩個左右兒子區間怎樣合併為這個區間?
首先對於每個節點,我們肯定要記錄這個區間的最大子段和,記作 。但是如果直接把兩個兒子的最大子段和取最大一定是錯的,因為一個區間的最大子段可能會跨過左右區間的分界點,而不是單獨分佈在左區間或者右區間。
為了維護這種情況,我們得多記兩個資訊:
以該區間左/右端點為起點的最大子段和,分別記作 。
如果能夠維護,那麼上述的另一種情況就也能被我們維護下來。我們只要把左區間的 加上右區間的 ,就能得出跨分界點的最大子段和。
但我們在合併時要怎樣維護 和 呢?由於兩個資訊是對稱的,我們拿 為例。
大區間的 一定等於左區間的 嗎?不一定。因為可能跨過分界點。
如果跨過分界點,其就是左區間的所有元素和加上右區間的 。所以我們還得在維護一個區間和,這個非常好維護。
因此這就是合併了,放個程式碼:
void Merge(R node &fa,R node &s1,R node &s2)
{
fa.gss=max(max(s1.gss,s2.gss),s1.rgss+s2.lgss);
fa.lgss=max(s1.lgss,s1.sum+s2.lgss);
fa.rgss=max(s2.rgss,s2.sum +s1.rgss);
fa.sum=s1.sum+s2.sum;
}
(區間 與 合併為 。)
理一理思路:
- 每個節點記錄以下四個資訊:。意義見上。
- 的維護
- 情況一:該區間的最大子段完全落在左子區間或右子區間。。
- 情況二·:該區間的最大子段跨過左右區間分界點。。
- 的維護( 同理)
- 情況一:該區間的 對應的子段完全落在左子區間。。
- 情況二:該區間的 對應子段跨過分界點。。
- 的維護: