Segment/Interval Tree - 2
by 林品諺
強,還要更強
• 我們現在已經會的東西有:
• 單點修改、區間詢問
• 區間修改、單點詢問
• 單點修改、單點詢問?
• 陣列O(1)解決
• 區間修改、區間詢問?
• 怎麼好像有點困難QAQ
範例題目
• 給你一個長度為N的序列以及Q個操作,操作分為兩種:
• 1. 把一個區間的每個數字都加上x
• 2. 詢問一個區間的最大值
• N, Q ≤ 100,000
題目說明
• 原始序列:
8 7 1 2 2 8
題目說明
• 詢問[3, 6]的最大值:8
8 7 1 2 2 8
題目說明
• 把[2, 5]加上7:
8 14 8 9 9 8
題目說明
• 詢問[3, 6]的最大值:9
8 14 8 9 9 8
just try it
• 先用我們已經會的線段樹知識嘗試一下
• 每個節點記錄它所對應的區間的最大值
• 區間詢問:一樣找到那logN個節點問一問答案,O(logN)
• 區間修改:把它轉成N個單點修改,O(NlogN)
• 複雜度爆炸了!
• 把題目轉成區間修改跟N個單點詢問顯然也沒有解決問題
• 如果那N個東西都被加上了同個數字,我們真的有需要把它當成N 個單點修改來操作嗎?
名詞解釋
• 「一個區間所對應的節點」
• 所有被該區間完全包含的節點們當中,深度最淺的那些節點
• 也就是區間詢問時會問到的那些節點
有的時候你不需要那麼勤勞
• 想想區間修改、單點詢問
• 把修改放在該區間所對應的節點上
• 詢問的時候會經過該點的祖先們,這時再去看剛剛的那些修改
• 區間詢問是不是也可以套用類似的想法?
• 對於每個修改,把它放在該區間所對應的節點上
• 區間修改:找到對應的節點們然後把修改貼上去,O(logN)
• 區間詢問:找到對應的節點們,把所記錄的data值加上修改的值 後取max,O(logN)
圖解
• 詢問[3, 6]的最大值:8
8 7 1 2 2 8
(0, 8)
(0, 8) (0, 8)
(0, 8) (0, 7) (0, 2) (0, 8)
(0, 7) (0, 1) (0, 2) (0, 8)
圖解
• 把[2, 5]加上7
8 14 8 9 9 8
(0, 14)
(0, 14) (0, 9)
(0, 8) (7, 7) (7, 2) (0, 9)
(0, 7) (0, 1) (7, 2) (0, 8)
圖解
• 詢問[3, 6]的最大值:9
8 14 8 9 9 8
(0, 14)
(0, 14) (0, 9)
(0, 8) (7, 7) (7, 2) (0, 9)
(0, 7) (0, 1) (7, 2) (0, 8)
實作
• chg: 修改的值, data: 我們要維護的東西(最大值)
區間修改
[a, b]:詢問區間, i:節點編號, [l, r]:該節點對應的區間, x:要加上去的數字
區間詢問
故事總是有個轉折
• 有了剛剛的技巧,看起來什麼區間加值、區間改值什麼的我們都 會做了
• 那如果兩個一起來呢?
例題二
• 給你一個長度為N的序列以及Q個操作,操作分為兩種:
• 1. 把一個區間的每個數字都加上x
• 2. 詢問一個區間的最大值
• 3. 把一個區間的每個數字都變成x
• N, Q ≤ 100,000
思考一陣
• 這不是一樣ㄇ……等等,真的一樣嗎?
• 如果開兩個變數分別存兩種修改,那我怎麼知道是先改再加、還 是先加再改?
• 到頭來好像又只能一個一個改了。
• 難道我們就要在這裡停下腳步了嗎?
繼續思考
• 問題發生在,兩種修改會互相影響。
• 假設所有加值操作都在左半邊,所有改值操作都在右半邊,那麼 就直接做就好了,對我們而言這世界就依舊美好。
• 兩種操作不影響的時候,就沒有不需要把修改壓到底了。
• 每一次修改只會動到O(logN)個節點。
• 如果我們在修改的時候,才順便把遇到的修改壓下去…?
• ……好像發現了什麼?
不小心得到了一個做法
• 修改的部分跟剛剛一樣,先放在該區間對應的節點上。
• 如果遞迴的路上發現這個節點有其他的修改資訊,就把那些資訊 往下推一層。
• 因為每次修改還是只會走過O(logN)個節點,所以複雜度不變。
• 詢問就照樣回答。
順序問題
• 剛剛的做法還有一個細節要注意,那就是兩種修改值的順序問題。
• 一個常見的做法是定義一個順序,比如說如果兩個修改值皆非 0(或任何表示他沒被修改的數字)時,假設是先改再加。
• 加值的時候就直接加上去。
• 改值的時候,因為改值這件事本身就會優先於加值,所以做改值 操作的時候要把加值的修改值改成0。
• 就這題而言,定義順序是先加再改不太能做。
實作
• chg1: 加值的修改, chg2: 改值的修改(以chg1=0, ch2=0表示沒有修改)
把修改往下壓
• 1
區間加值
• 1
區間改值
• 1
區間詢問
• 1
懶人標記
• 剛剛的技巧被稱為「懶人標記」(lazy tag)。
• 修改就直接先放在那,要用到的時候就路過順便壓一下。
• 可分治性
• 要能夠把修改先放著,就必須能夠在修改沒壓下去的時候就回答 詢問。
• 懶真是人類進步的原動力
• 只在你需要做事的時候才做事,有時候你其實不需要那麼勤勞。