【知识点】RMQ问题的ST表实现
$RMQ$问题:给定一个长度为$N$的区间,$M$个询问,每次询问$[L_i,R_i]$这段区间元素的最大值/最小值。
$RMQ$的高级写法一般有两种,即为线段树和$ST$表。
本文主要讲解一下$ST$表的写法。(以区间最大值为例)
$ST$表:一种利用$dp$思想求解区间最值的倍增算法。
定义:$f(i,j)$表示$[i,i+2^{j}-1]$这段长度为$2^{j}$的区间中的最大值。
预处理:$f(i,0)=a_i$。即$[i,i]$区间的最大值就是$a_i$。
状态转移:将$[i,j]$平均分成两段,一段为$[i,i+2^{j-1}-1]$,另一段为$[i+2^{j-1},i+2^{j}-1]$。
两段的长度均为$2^{j-1}$。$f(i,j)$的最大值即这两段的最大值中的最大值。
得到$f(i,j)=max\{f(i,j-1),f(i+2^{j-1},j-1)\}$。
void RMQ(int N){ /*注意外部循环从j开始, 因为初始状态为f[i][0], 以i为外层会有一些状态遍历不到。*/ for(int j=1;j<=20;j++) for(int i=1;i<=N;i++) if(i+(1<<j)-1<=N) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); }
查询:若需要查询的区间为$[i,j]$,则需要找到两个覆盖这个闭区间的最小幂区间。
这两个区间可以重复,因为两个区间是否相交对区间最值没有影响。(如下图)
当然求区间和就肯定不行了。这也就是$RMQ$的限制性。
因为区间的长度为$j-i+1$,所以可以取$k=log_2(j-i+1)$。
则$RMQ(A,i,j)=max\{f(i,k),f(j-2^{k}+1,k)\}$。
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; int a[100001],f[100001][20]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } void RMQ(int N){ for(int j=1;j<=20;j++) for(int i=1;i<=N;i++) if(i+(1<<j)-1<=N) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); } int main(){ int N=read(),M=read(); for(int i=1;i<=N;i++) a[i]=read(); for(int i=1;i<=N;i++) f[i][0]=a[i]; RMQ(N); while(M--){ int i=read(),j=read(); int k=(int)(log((double)(j-i+1))/log(2.0)); printf("%d\n",max(f[i][k],f[j-(1<<k)+1][k])); } return 0; }