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;
}
posted @ 2020-08-14 00:29  ke_xin  阅读(16)  评论(0编辑  收藏  举报