在RMQ问题(区间最值问题)中,ST算法就是倍增的产物。给定一个长度为N的序列,利用ST算法对其进行预处理 O ( n l o g n ) O(nlogn) O(nlogn)之后, O ( 1 ) O(1) O(1)地查询区间 [ L , R ] [L,R] [L,R]之间的最大或者最小值。
给定一个长度为 NN 的数列,和 M M 次询问,求出每一次询问的区间内数字的最大值。
朴素算法就是直接暴力地枚举区间,然后取 m i n , m a x min,max min,max这样在处理所有子区间和询问多个区间的时候,时间必定要超
初始化
此算法的实现最关键的部分是一个数组+倍增的思想
f
[
i
]
[
j
]
表
示
从
i
,
到
i
+
2
j
−
1
的
位
置
里
最
大
的
数
/
最
小
的
数
什
么
f[i][j]表示从i,到i+2^j-1的位置里最大的数/最小的数什么
f[i][j]表示从i,到i+2j−1的位置里最大的数/最小的数什么
对于整个的转移可以把 [ i , i + 2 j − 1 ] [i,i+2^j-1] [i,i+2j−1]的区间分成 [ i , i + 2 j − 1 − 1 ] [i,i+2^{j-1}-1] [i,i+2j−1−1], [ i + 2 j , i + 2 j − 1 ] [i+2^j,i+2^j-1] [i+2j,i+2j−1]两段之间取 m a x 和 m i n max和min max和min
f [ i ] [ j ] = m a x ( f [ i ] [ j − 1 ] , f [ i + ( 1 < < j − 1 ) ] [ j − 1 ] ) ; f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]); f[i][j]=max(f[i][j−1],f[i+(1<<j−1)][j−1]);
对于区间的判定, j j j绝对是 ≤ l o g 2 ( n ) \le log_2(n) ≤log2(n),所以就可以开两重循环去转移
查询
对于查询的话,就是和上面的思路一样把整个的区间分成两段来查询
对于查询的 f [ i ] [ k ] f[i][k] f[i][k], k ≤ l o g 2 ( 区 间 长 度 ) k \le log_2(区间长度) k≤log2(区间长度),那么就可以从头,和从末尾都搞一下,防止漏掉,而且两端区间覆盖是没有关系的~
细节见代码
#include<bits/stdc++.h> #define maxn 100010 using namespace std; inline int read() { int res=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();} while(isdigit(ch)){res=(res<<1)+(res<<3)+(ch&15);ch=getchar();} return res*f; } int n,m; int f[maxn][55]; int a[maxn]; inline void get() { for(int i=1;i<=n;i++)f[i][0]=a[i]; int k=log2(n)+1; for(int j=1;j<=k;j++) for(int i=1;i<=n;i++) f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]); } inline int search(int l,int r) { int k=log2(r-l+1); return max(f[l][k],f[r-(1<<k)+1][k]); } int main() { n=read();m=read(); for(int i=1;i<=n;i++)a[i]=read(); get(); while(m--) { int l,r;l=read();r=read(); printf("%d\n",search(l,r)); } return 0; }