ST算法(RMQ)

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;
}
上一篇:RocketMQ系列:搭建3m-noslave模式的rocketmq集群


下一篇:Java垃圾回收ygc代码模拟