貼一發上官松柏大犇寫的博客。
ST表(Sparse Table)是能夠區間查詢最值的數據結構,它採用倍增與dp的思想進行維護。
ST表複雜度:
- 空間複雜度
O(nlogn) - 預處理時間複雜度
O(nlogn) - 查詢時間複雜度
O(1)
ST表優勢:
- ST表的價值在於用較大的空間複雜度換取的
O(1) 的查詢複雜度,與線段樹O(n) ,O(n) ,O(logn) 不同,ST表的查詢部分併入主要部分程序後整體複雜度不會比線段樹高。所以是對程序效率的有效優化。
ST表劣勢:
- 有
O(nlogn) 的較大空間複雜度。 - 更新操作比較困難(即一般不支持更新,只能靜態查詢)。
預處理操作:
如果我們把每個k值當做一層,我們要考慮如何把
f[k][i] 用上一層的數據更新覆蓋。採用倍增的思想,我們定義:
f[k][i] 表示區間[i,i+2k−1] (持續2k 個)的最值。顯然上一層的覆蓋範圍正好是
f[k][i] 的一半,即2k−1 。那麼我們就可以用兩段剛好覆蓋住的區間[i,2k−1−1] 和[i+2k−1,(i+2k−1)+2k−1−1] 來收集結果了:
f[k][i]=min{f[k−1][i],f[k−1][i+2k−1]} 查詢操作:
同理,我們需要兩個已知的固定區間,能夠覆蓋(可以重合一部分)整個查詢區間。爲了方便查找,我們規定這兩個已知區間覆蓋的區間長度均爲2^k,並且兩個區間分別有一端爲L和R。那麼隨着這個k不斷增大,一定存在一個臨界點k,剛好覆蓋(或最少重合)這個區間。
當k滿足題意時,兩個區間分別爲
[L,L+2k−1] 和[R−(2k−1),R] ,此時只需要滿足R−(2k−1)≤L+2k−1 即可,化簡之後推出公式:
R−L+2≤2k+1,k≥log2(R−L+2)−1 小常數優化:
由於系統計算log非常慢,所以我們可以參考這篇文章對
2i 的處理方法,把所有數值的log2()全部打表出來。這裏的時間複雜度只需要O(n) 。
int f[S][M],Log2[M],a[M];
void init(int n){//ST表的預處理
int s=0;
for(int i=1;i<=n;i++){
f[0][i]=a[i];
if(i&(i-1))Log2[i]=s;//刪除最後一位二進制,如果剛好i=1<<t則變成0
else Log2[i]=s++;
}
for(int k=1;(1<<k)<=n;k++)
for(int i=1;i+(1<<k)-1<=n;i++)
f[k][i]=min(f[k-1][i],f[k-1][(1<<k-1)+i]);
}
int query(int L,int R){//ST表的查詢
int k=Log2[R-L+2]-1;
return min(f[k][L],f[k][R-(1<<k)+1]);
}