資料結構:ST表——解決RMQ問題
RMQ
RMQ 是英文 Range Maximum/Minimum Query 的縮寫,表示區間最大(最小)值。該類問題的解決方法有ST表,線段樹等。
題目引入
題目大意
給定 n n n個數,有 m m m個詢問,對於每個詢問,你需要回答區間 [ l , r ] [l,r] [l,r]中的最大值。
具體實現過程
-
預處理部分,令 d p [ i ] [ j ] dp[i][j] dp[i][j]表示區間 [ i , i + 2 j − 1 ] [i,i+2^j-1] [i,i+2j−1]的最大值,顯然 d p [ i ] [ 0 ] = a i dp[i][0]=a_i
第二維相當於倍增時跳了 2 j − 1 2^j-1 2j−1步,可將這 2 j − 1 2^j-1 2j−1步分成如下兩部分:
因此狀態轉移方程可以寫成: d p [ i ] [ j ] = m a x ( d p [ i ] [ j − 1 ] , d p [ i + 2 j − 1 ] [ j − 1 ] ) dp[i][j]=max(dp[i][j-1],dp[i+2^{j-1}][j-1]) dp[i][j]=max(dp[i][j−1],dp[i+2j−1][j−1]) -
查詢部分,對於每個詢問的區間 [ l , r ] [l,r] [l,r],可以把這個區間分成 [ l , l + 2 s − 1 ] [l,l+2^s-1]
演算法複雜度
ST 表基於倍增思想,預處理複雜度為 O ( n l o g n ) O(nlogn) O(nlogn),查詢的複雜度為 O ( 1 ) O(1) O(1)
模板程式碼
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int maxn=1e5+10;
int Logn[maxn];
int f[maxn][25];
//int型別資料的快讀
inline int read()
{
register int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=(x<<3)+(x<<1)+(c^48);
c=getchar();
}
return x*f;
}
//預處理log2(n)的值
void init()
{
Logn[1]=0;
Logn[2]=1;
for(int i=3;i<maxn;i++){
Logn[i]=Logn[i/2]+1;
}
}
int main()
{
// freopen ("input.txt" , "r" , stdin);
// freopen ("output.txt" , "w" , stdout);
int n=read(),m=read();
for(int i=1;i<=n;i++) f[i][0]=read();
init();
//預處理
for(int j=1;j<=21;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=read(),r=read();
int s=Logn[r-l+1];
printf("%d\n",max(f[l][s],f[r-(1<<s)+1][s]));
}
return 0;
}
總結
- 除 RMQ 以外,“區間按位和”、“區間按位或”、“區間 GCD”,ST 表都能高效地解決。
- ST 表能較好的維護“可重複貢獻”的區間資訊(同時也應滿足結合律),時間複雜度較低,程式碼量相對其他演算法很小。但是,ST 表能維護的資訊非常有限,不能較好地擴充套件,並且不支援修改操作。