ST
算法简介
在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这样在处理所有子区间和询问多个区间的时候,时间必定要超
ST算法
初始化
此算法的实现最关键的部分是一个数组+倍增的思想
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(区间长度),那么就可以从头,和从末尾都搞一下,防止漏掉,而且两端区间覆盖是没有关系的~
C o d e Code Code
细节见代码
#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;
}