ST表(稀疏表)
概述
ST表(Sparse Table,稀疏表)是一種主要用於求解可重復貢獻問題的數據結構,它基於倍增思想,通過預處理做到快速在線查詢,不支持修改
其中可重復貢獻問題是指對於一個元素來說,重復計算多次得到的答案不會改變的問題,例如求區間最值(RMQ),求區間最大公約數 (\(\gcd\))等,然而區間和,區間積等問題則不屬於可重復貢獻問題
過程
預處理
下面我們以區間最大值問題為例,介紹ST表的具體實現
問題:給定長度為 \(n\) 的序列和 \(q\) 次詢問,每次詢問給出 \(l,r\) ,你需要給出序列 \([l,r]\) 區間的最大值
數據范圍:對於 \(100\%\) 的數據,滿足 \(n\in[1,10^5]\),\(q\in[1,2\times10^5]\),\(l,r\in[1,n]\),序列中的任一元素 \(a_i\in[1,10^9]\)
首先我們考慮暴力,對於每次詢問都掃描 \([l,r]\),時間復雜度 \(\Theta(nm)\) 顯然T得飛起
那么該怎么解決呢?
考慮預處理出一部分區間的 \(max\),在詢問時只需要將覆蓋區間的 \(max\) 疊加就可以得到答案
那么應該預處理哪些區間呢?
考慮利用倍增思想,每次預處理出以 \(i\) 為起點,往后數 \(2^j\) 個數的區間 \(max\),也就是 \([i,i+2^j-1]\) 的區間 \(max\),不妨設 \(f_{i,j}\) 表示以 \(i\) 為起點,往后數 \(2^j\) 個數的區間 \(max\)
怎么合並出更大的區間呢?
可以想到把 \(2^j\) 拆分為兩個 \(2^{j-1}\) 相加,這里可能有些抽象,我們畫圖來進行更好的理解
那么有 \(f_{i,j}=max(f_{i,j-1},f_{i+2^{j-1},j-1})\),類似於DP,只需要遞推求解即可
由於需要大量計算對數,每次都使用 \(\log\) 函數十分低效,我們可以把 \(\log_2\) 全部預處理出來,使用的時候直接調用即可,具體方法見代碼
c++ code
#define MAXN 100005
int n,f[MAXN][21],lg2[MAXN];//記得定義為全局變量,初始值才是0
for(int i=2;i<=n;++i) lg2[i]=lg2[i>>1]+1;//預處理log_2
for(int i=1;i<=n;++i) scanf("%d",&f[i][0]);//f[i][0]直接讀入即可
for(int j=1;j<=lg2[n];++j)//最外層枚舉j,相當於每次都翻倍
for(int i=1,tmp=(1<<j)-1;i+tmp<=n;++i)//內層枚舉i,遍歷這一層數組,從上一層更新
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
查詢
對於每一次詢問區間 \([l,r]\),我們要怎么做才能處理出最大值呢?
我們先計算出滿足 \(2^k\le r-l+1<2^{k+1}\) 的 \(k\),也就是讓 \(2^k\) 小於區間長度的最大的 \(k\),這樣“從 \(l\) 開始的 \(2^k\) 個數”以及“以 \(r\) 結尾的 \(2^k\) 個數”就一定能覆蓋整個詢問區間 \([l,r]\),也就是“從 \(l\) 開始的 \(2^k\) 個數”以及“從 \(r-2^k\)+1 開始的 \(2^k\) 個數”
為什么呢?假設這兩段無法完全覆蓋 \([l,r]\) 那么有 \(2\times2^k<|[l,r]|\),那么 \(k\) 就不是讓 \(2^k\) 小於區間長度的最大的 \(k\),與我們的定義矛盾
“從 \(l\) 開始的 \(2^k\) 個數”以及“以 \(r\) 結尾的 \(2^k\) 個數”這兩段區間中可能會有重疊的部分,計算時會被重復統計,這就是為什么ST表只能用於處理可重復貢獻問題
同樣,再次上圖幫助理解
對於每次詢問,\(f_{l,k}\) 和 \(f_{r-2^k+1,k}\) 的最大值就是 \([l,r]\) 的最大值
c++ code
int q,l,r,k;
scanf("%d",&q);
while(q--)//一共q次詢問
{
scanf("%d%d",&l,&r);
k=lg2[r-l+1];
printf("%d\n",max(f[l][k],f[r-(1<<k)+1][k]));
}
復雜度分析
(注意!以下復雜度分析僅僅適用於RMQ問題,而區間最大公約數的復雜度不在此討論)
預處理時雙層循環,外層枚舉 \(j\),一共枚舉 \(log_2n\) 次,內層枚舉 \(i\),對於每一個 \(j\) 都要枚舉 \(n\) 次,所以預處理總復雜度為 \(\Theta(n\log n)\)
對於每個詢問,只用求一次 \(\max\),復雜度為 \(\Theta(1)\)
小結
ST表是一種主要用於求解可重復貢獻問題的數據結構,盡管能維護的東西有限並且不支持修改操作,但是因為其代碼簡短,思想清晰,使用方便的特點,在很多地方都有應用,例如求最近公共祖先等等
利用分塊思想,笛卡爾樹等其他算法或思想還可以對ST表進行優化或拓展,感興趣的讀者可以自行查閱
該文為本人原創,轉載請注明出處