这两天学习RMQ的离线算法学到了ST表
相比于线段树,他的效率更高,预处理O(NlogN),查询O(1)
预处理
原理:动态规划
用a[1…n]表示一组数f[i,j]是a[i]到a[i+2^j-1]的最大值
也就是以a[i]为起点,连续2^j个数的max
f[i][j]=max(f[i][2(j-1)],f[i+2(j-1)][j-1]
void ST() {
log[0]=-1; //这样才能使log[1]=0;
FOR(i,1,n) log[i]=log[i>>1]+1,f[i][0]=a[i];
FOR(j,1,log[n]) FOR(i,1,n)
f[i][j]=max(f[i][j-1],f[i+(1<<j)+1][j-1]); //位运算加速
}
询问
询问区间[l,r]之间最大值,首先找到最大的x满足2x<=r-l+1,那么区间就满足[l,l+2x-1]U[r-2x+1,r]
于是,他的最大值就是max([l,l+2x-1],[r-2x+1,r])
void ask(int l,int r) {
int x=log[r-l+1];
printf("%d",max(f[l][s],f[r-(1<<s)+1][s]);
}
附luogu3865AC代码
https://www.luogu.org/problemnew/show/P3865#sub
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int f[100001][40],a,x,LC,n,m,p,len,l,r;
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
scanf("%d",&a);
f[i][0]=a;
}
LC=(int)(log(n)/log(2));
for(int j=1; j<=LC; j++)
for (int i=1; i<=n-(1<<j)+1; i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1; i<=m; i++) {
scanf("%d%d",&l,&r);
p=(int)(log(r-l+1)/log(2));
printf("%d\n",max(f[l][p],f[r-(1<<p)+1][p]));
}
return 0;
}