O(n)-O(1) 線性 RMQ 學習筆記

dengchengyu發表於2024-08-18

O(n)-O(1) 線性 RMQ 學習筆記

\(O(n)\) 預處理,\(O(1)\) 查詢的 RMQ(區間最值)演算法。

而我們正常 ST 表處理 RMQ 只能做到 \(O(n \log n)-O(1)\)

用四毛子演算法可以做到 \(O(n\log\log n)-O(1)\)

四毛子演算法:對原序列分塊,塊長為 \(O(\log n)\),塊之間做 ST 表,每個塊內做 ST 表(不能預處理字首和字尾最值,因為可能有左右端點在一個塊內的情況)。

1.建笛卡爾樹

我們對原序列建出笛卡爾樹,至於是大根還是小根依據具體題目。

我們可以使用單調棧 \(O(n)\) 建出笛卡爾樹。

笛卡爾樹的性質:區間 \([L,R]\) 最值位於笛卡爾樹上 \(L,R\)\(LCA\)

於是區間最值轉化為了求 \(LCA\)

2.尤拉序轉化 LCA

對笛卡爾樹建出尤拉序。

尤拉序:初始一個空的序列,在 \(dfs\) 的過程中,每個點進入時將它加入序列末尾,離開時將它的父親加入序列末尾,特別地,根節點離開時不加入它的父親(它沒有父親),尤拉序是一個長為 \(2n-1\) 的序列。

尤拉序的性質:設 \(first_i\) 表示點 \(i\) 第一次在尤拉序中出現的位置,\(last_i\) 為最後一次出現的位置,則 \(i,j(first_i<first_j)\)\(LCA\) 為尤拉序上 \([first_i,last_j]\) 區間內深度最小的點。

這是一個 \(\pm1\) RMQ 問題,也就是相鄰兩個元素的值剛好相差 \(1\)

以上的主要思想就是把一般 RMQ 問題轉化為 \(\pm1\) RMQ 問題。

3.\(\pm1\) RMQ 問題

\(t\) 為尤拉序列的長度,即 \(t=2n-1\)

我們採用 分塊 + ST 表 + 散塊狀壓 來解決這個問題,其中 分塊 + ST 表 就是四毛子演算法的思想。

取塊長 \(B=\lceil\frac{\log t}{2}\rceil\),對尤拉序列分塊,則有 \(t/B\) 個塊。

對於每個塊之間做 ST 表,時間複雜度 \(O(t)\)

而對於散塊,我們並不能預處理每個塊的字首和字尾最值,因為可能出現左端點和右端點在同一個塊裡的情況。

考慮到一個塊的差分陣列只有 \(2^{B-1}\) 種,預處理每種差分陣列的任意左端點和右端點的情況,時間為 \(O(2^{B}B^2)\)

代入 \(B\) 得到 \(O(\sqrt t\log ^2t)\) 略小於 \(t\)

具體來說狀壓一個塊的差分陣列,深度加一則這一位為 \(1\),深度減 \(1\) 則這一位為 \(0\)

於是 \(O(n)-O(1)\) RMQ 就完成了(不過資料隨機且 \(n\) 較小時跑得比樸素演算法慢,甚至可能比每次暴力還慢)。

相關文章