学习笔记:RMQ问题ST表

引入

RMQ问题是求一个区间的最大值问题,通常有很多的询问。那么我们要如何解决这种问题呢?
暴力:当然可以O(n*m)n为个数,m为询问个数,显然不行。
线段树:建树需要 O(logn)的时间,询问需要 O(logn)时间,线段树的算法
是 O(n*logn)+O(logn)的。
Sparse Table(ST):它是一种基于倍增思想的算法,O(n*logn)的预处理,O(1)回
答每个询问,所以时间复杂度为 O(n*logn)+O(m),空间复杂度为 O(n*logn)。
线段树的方法已经介绍,今天我们来介绍ST 算法。

原理

我们用一个2维数组f[ i ][ j ]表示从a[ i ]开始2的j次方个的数。那么我们就可以从中间拆开分为,两个2的j-1次方。基于这样的倍增思想,看此图。
在这里插入图片描述
f[1][0]就是从1开始1个数的最大值,即a[1]自己;
f[2][2]就是从2开始4个数的最大值,即99;
那如果是f[5][3]那就可以拆成2段,f[5][2]和f[9][2]的最大值;

那么这样我们可以看作是DP,
状态转移方程:f[ i ][ j ]=max(f[ i ][ j-1 ],f[ i+2(j-1) ][ j-1 ]),
边界条件:f[ i ][0]=a[ i ],
通过这个我们就可以预处理每一段的最大值了。
要注意的是,f[ i ][ j ]的j的范围最大就应是logn(拿计算机计算最大值)
应该如何计算logn呢?因为计算机中只有ln(自然对数),所以我们要用换底公式
在这里插入图片描述
1.预处理
而c++中自带有函数log( ),便可快乐地预处理了。

for(int i=1;i<=n;i++)
	f[i][0]=a[i];    //初始化
	
for(int j=1;j<=log(n)/log(2);j++) //最大2的次方 
	for(int i=1;i+(1<<j)-1<=n;i++) //范围r-l+1
	{
    
    
		f[i][j]=max(f[i][j-1],f[i+(1<<j)-1][j-1]);//二分 
	}

2.询问操作
对于询问[L,R],我们要查询[L,R]=[L,L+2k-1]∪[R-2k+1,R]这2段分开的最大值,虽然这2段会有交叉,但最大值不用管。
其中L+2k-1<=R,即R-L+1>=2k所以,我们要找出最大k值,即k=loge(R-L+1)/loge 2。

inline int query(int l,int r)
{
    
    
	int k=int(log(r-l+1)/log(2));//强制转换为int
	return max(f[l][k],f[r-(1<<k)+1][k]); 
}

模板

传送门luoguP3865

#include<bits/stdc++.h>
using namespace std;

const int N=1e7+5;
int a[N],f[N][30],n,m;//计算得出最大k=30

#define num ch-'0'//快读
void get(int &res)
{
    
    
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    (flag)&&(res=-res);
}

inline void Build()
{
    
    
	for(int i=1;i<=n;i++)
		f[i][0]=a[i];
	
	for(int j=1;(1<<j)<=n;j++)
		for(int i=1;i+(1<<j)-1<=n;i++)
		{
    
    
			f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
		}
}

inline int query(int l,int r)
{
    
    
	int k=int(log(r-l+1)/log(2));
	return max(f[l][k],f[r-(1<<k)+1][k]);
}

int main()
{
    
    
	get(n);get(m);
	for(int i=1;i<=n;i++)
	{
    
    
		get(a[i]);
	}
	
	Build();
	
	for(int i=1;i<=m;i++)
	{
    
    
		int l,r;
		scanf("%d%d",&l,&r);
		printf("%d\n",query(l,r));
	}
	return 0;
}

ST表就基本介绍完了,但ST表只能离线查询,要修改的话就用线段树。

Guess you like

Origin blog.csdn.net/pigonered/article/details/120810119