Segment/Interval Tree in class
by 林品諺
知己知彼,百戰百勝
• 大家都看過影片了嗎?
• 線段樹能做哪些事情?
• RMQ(Range Maximim/Minimum Query)
• 區間和
• 除此之外,線段樹還有什麼應用呢?
小故事
• 為什麼我要特別介紹普通線段樹的應用呢?
• 在我懵懂無知的童年時光,誤入歧途,學了線段樹。
• 那時每個人都對我說:線段樹可以做很多事情。
• 可是我只會RMQ跟區間和。
• 那時我常常問別人:線段樹除了算區間最大值以外還能做麽呀?
• 好幾個人都跟我說:很多呀,比如說算區間和。
• 除此之外呢?
前情提要
• 線段樹不只是區間和與最大值
• 區間和不只是區間和
• 最大值不只是最大值
• 離線大法
前情提要
• 線段樹不只是區間和與最大值
• 區間和不只是區間和
• 最大值不只是最大值
• 離線大法
線段樹不只是區間和與最大值
• sprout 2016 第二次認證考 p? 「我就知道會這樣」
• 給你一個長度為 N 的序列 a,支援以下兩種操作:
• 1. 修改一個點的值 (單點修改)
• 2. 對於一個區間[l, r],詢問找到一組(i, j)滿足l ≤ i ≤ j ≤ r,最大 化a[j]-a[i]
• 原題的題敘是給你某支股票 N 天內的股價,問你在第 l 天到第 r 天之間,如果只能買入與賣出一張股票,最多可以賺多少錢。
• N ≤ 200,000
我不知道標題要打什麼
• 單點修改&區間詢問,這一定是線段樹!
• 如果可以把問題切成小問題,就可以用線段樹維護!
• D&C精神:不管怎樣先切成兩部份,可以merge就可以做
• 答案有三種可能:
• 1. (i, j)都在左半邊的
• 2. (i, j)都在右半邊的
• 3. i 在左半邊,但j在右半邊的
讚嘆分而治之的力量
• 斯斯有三種,答案也有三種,生病不要去三總:
• 1. (i, j)都在左半邊的
• 2. (i, j)都在右半邊的
• 3. i 在左半邊,但j在右半邊的
• 1., 2.: 直接遞迴下去
• 3.: 最好的(i, j)是左半邊最小的a[i]與右半邊最大的a[j]
• 所以對於每個區間只要維護三個東西:
• 區間的最大值
• 區間的最小值
• 區間的最佳(i, j)
• 就解出來了~~
這題給我們的啟示
• 只要有類似這種「可以切成小問題」的性質就可以用線段樹!
• 更精確地來說,只要merge夠快就行。
前情提要
• 線段樹不只是區間和與最大值
• 區間和不只是區間和
• 最大值不只是最大值
• 離線大法
區間和不只是區間和
• 有一個隊列,一開始是空的
• 有 N 個人依序過來排隊,第 i 個人會排在第 a[i] 個人後面 (a[i] == 0就表示第 i 個人排在隊伍的最前面)
• 請你輸出最後的隊伍長相
• N ≤ 200,000
思考
• 要怎麼支援在第k跟k+1人中間插入一個數字?
• 自己刻一個平衡樹!
• 可以,但是不好,太痛苦了
• 對於第 N 個人而言,他一定是在最後的隊伍的第a[N]個人後面
• 對於第 N-1個人而言,他一定是在扣掉第 N 個人的隊伍的第 a[N-1]個人後面
• 時光倒流!!
• 只要能找到挖掉幾個空格後的第k個人在哪裡就好了
所以要怎麼做?
• 時光倒流後,問題可以轉化這樣:
• 問你當前的序列裡的第 k個人是誰 (區間詢問?)
• 把第k個人拔掉 (單點修改)
• 要怎麼找到做到這件事呢?
• 把一個序列的每個位子都初始化為 1 ,然後開一棵維護總和的線 段樹
• 對於一個區間,如果他左半邊的1的數量不小於k,就表示第k個 人在左半邊,反之就在右半邊
• 遞迴下去就好了!
• 修改就只是普通的維護總和的線段樹的單點修改
前情提要
• 線段樹不只是區間和與最大值
• 區間和不只是區間和
• 最大值不只是最大值
• 離線大法
最大值不只是最大值
• 給你一個多重集合S,一開始是空的
• 有三種操作:
• 1. 把x加入到集合中
• 2. 把x從集合裡拔掉
• 3. 回答S裡面出現最多次的數字(眾數),如果有多個就輸出最小的
• 操作數 ≤ 200,000, x ≤ 200,000
思考
• 一個想法:開一個陣列,a[i]表示i在集合S裡面出現了幾次
• 目標,找到最大的a[i],有多個a[i]時選最小的i
• 其實有個用STL map就可以爽爽寫掉的作法,不過用到了一個神 奇的技巧
• 可以用線段樹找到最左邊的最大值嗎…?
數值線段樹
• 我們把「把數值當作索引值」的線段樹稱為數值線段樹
• 對a[i]建一棵維護最大值的線段樹
• 問題就變成在線段樹上找最左邊的最大值
• 用類似「區間和不只是區間和」的方法在線段樹上走,就可以找 到答案
前情提要
• 線段樹不只是區間和與最大值
• 區間和不只是區間和
• 最大值不只是最大值
• 離線大法
離線大法
• 給你一個長度為 N 的序列 a 以及 Q 個詢問
• 每個詢問包含一組(l, r, x)
• 詢問一個區間[l, r]裡有幾個比x大的數字
思考
• 這東西其實可以被持久化線段樹揍掉,但我不是要講這個
• 如果只是要問區間有幾個數字比某個 x 大呢?
• 開一個陣列 b ,b[i]=(a[i]>=x ? 1 : 0)
• 對 b 建一棵維護總和的線段樹
• 如果 x 照順序給的話有好事:直接維護 b 陣列&對應的線段樹,
每個 i 只會被改到一次,複雜度會是好的
• 如果詢問的 x 一直亂跳呢?
• 離線!
偷看後面的詢問
• 我們可以一口氣把所有的詢問讀進來,然後照 x 的大小排序
• 我們稱這種把所有詢問都讀進來之後再一起處理的技巧稱為「離 線」,反之如果是看到一個詢問就回答就是「在線」
• 不過有些題目會強制在線,比如說要你回答一個詢問之後才給你 下一個詢問