Segment/Interval Tree in class

21  Download (0)

Full text

(1)

Segment/Interval Tree in class

by 林品諺

(2)

知己知彼,百戰百勝

•  大家都看過影片了嗎?

•  線段樹能做哪些事情?

•  RMQ(Range Maximim/Minimum Query)

•  區間和

•  除此之外,線段樹還有什麼應用呢?

(3)

小故事

•  為什麼我要特別介紹普通線段樹的應用呢?

•  在我懵懂無知的童年時光,誤入歧途,學了線段樹。

•  那時每個人都對我說:線段樹可以做很多事情。

•  可是我只會RMQ跟區間和。

•  那時我常常問別人:線段樹除了算區間最大值以外還能做麽呀?

•  好幾個人都跟我說:很多呀,比如說算區間和。

•  除此之外呢?

(4)

前情提要

•  線段樹不只是區間和與最大值

•  區間和不只是區間和

•  最大值不只是最大值

•  離線大法

(5)

前情提要

•  線段樹不只是區間和與最大值

•  區間和不只是區間和

•  最大值不只是最大值

•  離線大法

(6)

線段樹不只是區間和與最大值

•  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

(7)

我不知道標題要打什麼

•  單點修改&區間詢問,這一定是線段樹!

•  如果可以把問題切成小問題,就可以用線段樹維護!

•  D&C精神:不管怎樣先切成兩部份,可以merge就可以做

•  答案有三種可能:

•  1. (i, j)都在左半邊的

•  2. (i, j)都在右半邊的

•  3. i 在左半邊,但j在右半邊的

(8)

讚嘆分而治之的力量

•  斯斯有三種,答案也有三種,生病不要去三總

•  1. (i, j)都在左半邊的

•  2. (i, j)都在右半邊的

•  3. i 在左半邊,但j在右半邊的

•  1., 2.: 直接遞迴下去

•  3.: 最好的(i, j)是左半邊最小的a[i]與右半邊最大的a[j]

•  所以對於每個區間只要維護三個東西:

•  區間的最大值

•  區間的最小值

•  區間的最佳(i, j)

•  就解出來了~~

(9)

這題給我們的啟示

•  只要有類似這種「可以切成小問題」的性質就可以用線段樹!

•  更精確地來說,只要merge夠快就行。

(10)

前情提要

•  線段樹不只是區間和與最大值

•  區間和不只是區間和

•  最大值不只是最大值

•  離線大法

(11)

區間和不只是區間和

•  有一個隊列,一開始是空的

•  有 N 個人依序過來排隊,第 i 個人會排在第 a[i] 個人後面 (a[i] == 0就表示第 i 個人排在隊伍的最前面)

•  請你輸出最後的隊伍長相

•  N ≤ 200,000

(12)

思考

•  要怎麼支援在第k跟k+1人中間插入一個數字?

•  自己刻一個平衡樹!

•  可以,但是不好,太痛苦了

•  對於第 N 個人而言,他一定是在最後的隊伍的第a[N]個人後面

•  對於第 N-1個人而言,他一定是在扣掉第 N 個人的隊伍的第 a[N-1]個人後面

•  時光倒流!!

•  只要能找到挖掉幾個空格後的第k個人在哪裡就好了

(13)

所以要怎麼做?

•  時光倒流後,問題可以轉化這樣:

•  問你當前的序列裡的第 k個人是誰 (區間詢問?)

•  把第k個人拔掉 (單點修改)

•  要怎麼找到做到這件事呢?

•  把一個序列的每個位子都初始化為 1 ,然後開一棵維護總和的線 段樹

•  對於一個區間,如果他左半邊的1的數量不小於k,就表示第k個 人在左半邊,反之就在右半邊

•  遞迴下去就好了!

•  修改就只是普通的維護總和的線段樹的單點修改

(14)

前情提要

•  線段樹不只是區間和與最大值

•  區間和不只是區間和

•  最大值不只是最大值

•  離線大法

(15)

最大值不只是最大值

•  給你一個多重集合S,一開始是空的

•  有三種操作:

•  1. 把x加入到集合中

•  2. 把x從集合裡拔掉

•  3. 回答S裡面出現最多次的數字(眾數),如果有多個就輸出最小的

•  操作數 ≤ 200,000, x ≤ 200,000

(16)

思考

•  一個想法:開一個陣列,a[i]表示i在集合S裡面出現了幾次

•  目標,找到最大的a[i],有多個a[i]時選最小的i

•  其實有個用STL map就可以爽爽寫掉的作法,不過用到了一個神 奇的技巧

•  可以用線段樹找到最左邊的最大值嗎…?

(17)

數值線段樹

•  我們把「把數值當作索引值」的線段樹稱為數值線段樹

•  對a[i]建一棵維護最大值的線段樹

•  問題就變成在線段樹上找最左邊的最大值

•  用類似「區間和不只是區間和」的方法在線段樹上走,就可以找 到答案

(18)

前情提要

•  線段樹不只是區間和與最大值

•  區間和不只是區間和

•  最大值不只是最大值

•  離線大法

(19)

離線大法

•  給你一個長度為 N 的序列 a 以及 Q 個詢問

•  每個詢問包含一組(l, r, x)

•  詢問一個區間[l, r]裡有幾個比x大的數字

(20)

思考

•  這東西其實可以被持久化線段樹揍掉,但我不是要講這個

•  如果只是要問區間有幾個數字比某個 x 大呢?

•  開一個陣列 b ,b[i]=(a[i]>=x ? 1 : 0)

•  對 b 建一棵維護總和的線段樹

•  如果 x 照順序給的話有好事:直接維護 b 陣列&對應的線段樹,

每個 i 只會被改到一次,複雜度會是好的

•  如果詢問的 x 一直亂跳呢?

•  離線!

(21)

偷看後面的詢問

•  我們可以一口氣把所有的詢問讀進來,然後照 x 的大小排序

•  我們稱這種把所有詢問都讀進來之後再一起處理的技巧稱為「離 線」,反之如果是看到一個詢問就回答就是「在線」

•  不過有些題目會強制在線,比如說要你回答一個詢問之後才給你 下一個詢問

Figure

Updating...

References

Related subjects :