1. 程式人生 > >【轉載】線段樹 區間合並 小結

【轉載】線段樹 區間合並 小結

最大值 總結 兩個 註釋 長度 或操作 可能 地址 組合

原地址:https://blog.csdn.net/sunyutian1998/article/details/79618316

個人感覺區間合並是線段樹各種應用中變形最多 也是比較難琢磨的一種

(以下以求01序列中最長連續1為例)

tree[cur].left代表以區間左端點為起點的連續段的長度 tree[cur].right代表右邊 tree[cur].all代表該區間內最長的連續段

通過這三個變量的組合 對於每一個區間一般有四個值供我們使用

1 tree[cur].left代表以該區間左端點為起點的連續段的長度(左連續段)

2 tree[cur].right代表以該區間右端點為終點的連續段的長度(右連續段)

3 tree[2*cur].right+tree[2*cur+1].left 代表包含該區間中點的連續段的長度 這是個隱含值(中間連續段)

4 tree[cur].all代表該區間內最長連續段 其值不止要從左右子節點的最長連續段中擇最大 還要考慮該區間的中間連續段即 tree[cur].all=max(tree[2*cur].right+tree[2*cur+1].left,max(tree[2*cur].all,tree[2*cur+1].all));

   對於tree[cur].all的取值可以這樣理解 如果兩個子區間合並後產生了新的連續段 那一定是tree[2*cur].right+tree[2*cur+1].left的值 即中間連續段 如果沒有那就只能在兩個子區間的最長連續段擇優了

區間合並關鍵在於pushup函數的構造 詳見代碼註釋(以求01序列中最長連續1為例)

void pushup(int cur)
{
    tree[cur].left=tree[2*cur].left;//當前區間的左區間的左端點肯定也是當前區間的左端點 所以以當前區間的左端點為起點的最長連續1長度肯定包含其左區間的對應值 這一部分值先記下來
    if(tree[cur].left==tree[2*cur].r-tree[2*cur].l+1) tree[cur].left+=tree[2*cur+1].left;//再判斷一下當前這些連續的1是不是已經貫穿了整個左區間 和有區間接軌 如果是的話 必須把右孩子的對應值也加上(如果右孩子對應區間全為1 那麽當前區間也會全為1 這種情況也是很有可能的)
 
    /*
    比如[1,8]區間 (1 1 1 1) (1 1 0 1)
    在未pushup時
    tree[2*cur].left==4,tree[2*cur].right==4,tree[2*cur].all==4
    tree[2*cur+1].left==2,tree[2*cur+1].right==1,tree[2*cur+1].all==2
    執行完tree[cur].left=tree[2*cur].left後tree[cur].left==4 但顯然以當前區間的左連續段的長度還需要算上右區間中的一部分
    所以要執行tree[cur].left+=tree[2*cur+1].left 然後tree[cur].left==6即為正確結果
    */
 
    tree[cur].right=tree[2*cur+1].right;//右連續段的處理同上
    if(tree[cur].right==tree[2*cur+1].r-tree[2*cur+1].l+1) tree[cur].right+=tree[2*cur].right;
 
    tree[cur].all=max(tree[2*cur].right+tree[2*cur+1].left,max(tree[2*cur].all,tree[2*cur+1].all));
    //顯然當前區間最長連續段要從左右區間的最長連續段取最大值 但這樣就足夠了嗎?
    //tree[cur].all的值不止要從左右子區間的最長連續段中擇最大 還要考慮該區間隱含的"中間連續段"
    /*
    比如[1,12]區間 (1 1 1 0 1 1) (1 1 0 1 1 1)
    在未pushup時
    tree[2*cur].left==3,tree[2*cur].right==2,tree[2*cur].all==3
    tree[2*cur+1].left==2,tree[2*cur+1].right==3,tree[2*cur+1].all==3
    只考慮從左右區間的最長連續段取最大值的話 即執行tree[cur].all=max(tree[2*cur].all,tree[2*cur+1].all)之後tree[cur].all==3
    但顯然中間有四個連續的1被漏掉了
    */
    return;
}

下面是做過的一些類型題總結

1 求一塊滿足條件的最左邊的空白空間 poj3667

2 求某個元素所在連續段的長度(也可求左右端點) hdu1540

3 求某個連續段的起始位置 hdu2871

4 區間合並在掃描線求周長中的應用 hdu1828

5 區間合並與異或操作結合 以及求整個區間內最長連續段的長度 hdu3911 hdu3397

6 求區間最長連續上升序列 hdu3308

【轉載】線段樹 區間合並 小結