ST表
ST表
RMQ
(Range Minimum/Maximum Query),即区间最值查询。对于长度为\(n\)的数列\(A\),回答若干询问RMQ(A,i,j)(i, j<=n),返回数列\(A\)中下标在\([i,j]\)之间的最小/大值。如果只有一次询问,那样只有一遍for就可以搞定,但是如果有许多次询问就无法在很快的时间处理出来。在这里介绍一个在线算法。ST(Sparse Table)算法是一个非常有名的在线处理RMQ问题的算法,它可以在\(O(nlogn)\)时间内进行预处理,然后在\(O(1)\)时间内回答每个查询。
与线段树相比,预处理复杂度同为O(nlogn),查询时间上,ST表为O(1),线段树为O(logn)
假设a数组为:
1, 3, 6, 7, 4, 2, 5
1.首先做预处理(以处理区间最小值为例)
设\(mn[i][j]\)表示从第\(i\)位开始连续 \(2^j\) 个数中的最小值。例如\(mn[2][1]\)为第2位数开始连续2个的数的最小值,即3, 6之间的最小值,即\(mn[2][1]=3;\)
之后我们很容想到递推方程:
\(mn[i][j] = min(mn[i][j - 1], mn[i + (1 << j - 1)][j - 1])\)
详细证明见这里
代码(结构体封装版):
#include <iostream>
#include <cstdio>
using namespace std;
const int N=2e5;
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,q,l,r;
int mx[N][25];
struct RMQ{
int log2[N];
void init(){
log2[0]=-1;
for(int i=1;i<=n;i++) log2[i]=log2[i>>1]+1;
for(int j=1;j<20;j++)
for(int i=1;i+(1<<j)<=n+1;i++)
mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
}
int query(int ql,int qr){
int k=log2[qr-ql+1];
return max(mx[ql][k],mx[qr-(1<<k)+1][k]);
}
}rmq;
int main(){
n=read();q=read();
for(int i=1;i<=n;i++)
mx[i][0]=read();
rmq.init();
for(int i=1,l,r;i<=q;i++){
l=read();r=read();
printf("%d\n",rmq.query(l,r));
}
return 0;
}
代码(一般)
https://www.luogu.com.cn/problem/P1816
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=100005;
inline int read() {
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return f*x;
}
int n,m;
int f[N][20],lg[N],ans[N];
int main() {
m=read();n=read();
memset(f,0x3f,sizeof(f));
for(int i=1;i<=m;i++) f[i][0]=read();
for(int i=2;i<N;i++) lg[i]=lg[i>>1]+1;
for(int j=1;j<=19;j++)
for(int i=1;i+(1<<j)-1<=m;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
int x,y;
for(int i=1;i<=n;i++) {
x=read();y=read();
int k=lg[y-x+1];
ans[i]=min(f[x][k],f[y-(1<<k)+1][k]);
}
for(int i=1;i<=n;i++)
printf("%d ",ans[i]);
return 0;
}