ST表——楊子曰數據結構
今天我們來曰一種O(1)查詢的數據結構——ST表
它,就是RMQ問題的剋星!
給你一個數列,對於詢問[l,r],輸出區間[l,r]內的最大值(你喜歡最小值也可以啦!)
這……我用線段樹O(log n)(搞一搞不久好了嗎?
不好!!!我們是追求速度的人,由於它沒有更新操作,我們可以做到O(1)查詢
我們的ST表閃亮登場!!
(在下面的東西前你也可以先閱讀一下:談談最近公共祖先(LCA)——楊子曰算法,有助於理解)
黑餵狗:
ST表最最重要的思想就是——倍增
我們開一個數組 表示區間內的最大值,也就是從開始數個數裏面的最大值
Look at the 圖,比如這就是表示的最大值區間:
好的,理解了這個數組的含義以後我們來看,怎麼求出這個倍增數組?
掃一遍
初始值很簡單
那如何轉移呢?
其實就一句話(和倍增求LCA一樣):
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
很好理解吧!
我們來舉個例子,比如我們現在就是要求,我們可以從和轉移過來,也就是要把我們要求的區間一剖爲二:
完美,我們成功把預處理的複雜度做到了O(n log n):
for (int i=1;i<=n;i++){
f[i][0]=a[i];
}
for (int j=1;j<=20;j++){//注意i和j的循環順序
for (int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
好的,我們現在已經得到了f數組,那我們如何利用它去求出區間最大值呢?
區間的長度不一定是2的整數次冪呀!腫麼辦!!!
太簡單了,比如我們要查詢區間的最大值,我把它變成:
兩段區間重疊在一起完全沒有關係的呀!
也就是說,如果查詢的區間是,我們給出的答案就是:
仔細地研究一下這個式子,它講的其實就是找到一個小於區間長度(r-l+1)的最大2的整數次冪,作爲我們重疊的兩段小區間的長度,然後取靠着左端點的這段,和靠着右端點的這段去一個max就行了!
OK,完事
c++代碼(洛谷【P3865】):
#include<bits/stdc++.h>
using namespace std;
const int maxn=100005;
int a[maxn],f[maxn][35],lg[maxn];
int main(){
int n,m;
scanf("%d%d",&n,&m);
lg[1]=0;
for (int i=2;i<=n;i++) lg[i]=lg[i/2]+1;
for (int i=1;i<=n;i++){
scanf("%d",&a[i]);
f[i][0]=a[i];
}
for (int j=1;j<=20;j++){
for (int i=1;i+(1<<j)-1<=n;i++){
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
}
while(m--){
int l,r;
scanf("%d%d",&l,&r);
int tmp=lg[r-l+1];
printf("%d\n",max(f[l][tmp],f[r-(1<<tmp)+1][tmp]));
}
return 0;
}
ST表:哈哈哈,打不過我吧,沒辦法我就是這麼強大!
線段樹:你動態更新一個個我看看
ST表:……
於HG機房