# 進階資料結構與莫隊算法—稀疏表
###### tags: `108 師大附中校隊培訓`
###### wiwiho
---
前綴和在經過 $O(n)$ 預處理後
可以 $O(1)$ 求得區間和
----
但顯然這樣的方式不能用在區間最大或最小值上
那麼有沒有辦法可以在經過預處理後
$O(1)$ 得到區間最大或最小值呢?
----
一樣用切塊的方法來想
但是我們要把塊「合併」
兩個元素可以合成一個長度為 $2$ 的塊
兩個長度為 $2$ 的塊可以合成長度為 $4$ 的塊……
兩個長度為 $2^{i-1}$ 的塊可以合成長度為 $2^i$ 的塊
而且有很多塊是重疊的
---
## 用一個 DP 的方式來做
----
令 $s[i][j]$ 為以元素 $i$ 為起點
長 $2^j$ 的塊中的最大值或最小值
也就是說
這個塊的範圍是 $[i, i+2^j-1]$
(會超出範圍的就不用算,也用不到)
----
長度 $2^j$ 的塊可以
分割成兩個長度 $2^{j-1}$ 的塊
起點分別是 $i$ 和 $i + 2^{j-1}$
----
$s[i][j]$ 的值是
$s[i][j-1]$ 和 $s[i + 2^{j-1}][j-1]$
取最大或最小
----
$j=0$ 時
$2^j=1$,因此以 $i$ 為起點
$2^j$ 大小的塊長度只有 $1$
內容也只有第 $i$ 個元素
----
$s[i][0]$ 就是第 $i$ 個元素
----
當我們要求 $[l,r]$ 的最大或最小值
就用兩個長 $2^{\lfloor \log_2 (r - l + 1) \rfloor}$
的塊來湊出答案就好
其中一個的起點是 $l$
另一個起點是 $r-2^{\lfloor \log_2 (r - l + 1) \rfloor} + 1$
----
令 $k=\lfloor \log_2 (r - l + 1) \rfloor$
可以用 $s[l][k]$ 和 $s[r-2^k+1][k]$
來得到我們想要的答案
----
先預處理整個 $s$
----
我們需要求出以每個元素為起點
各個 $2^j$ 長的塊的最大或最小值
$j$ 的最大值應該要是多少?
----
會用到的最大的 $j$
會出現在詢問區間是整個數列的時候
$j=\lfloor \log_2 n \rfloor$
----
$j$ 的範圍只需要 $[0,\lfloor \log_2 n \rfloor]$
→預處理複雜度是 $O(n \log n)$
---
稀疏表不能做修改
因為一個元素會被好幾個塊包含
數量很多,沒效率
{"metaMigratedAt":"2023-06-15T01:14:34.053Z","metaMigratedFrom":"Content","title":"進階資料結構與莫隊算法—稀疏表","breaks":false,"contributors":"[{\"id\":\"02353542-5acb-4a66-abd0-128b1af24abb\",\"add\":1198,\"del\":0}]"}