网址:https://www.luogu.org/problem/P3865
题意:
静态区间最大值,且需要在$O(nlgn)$预处理,$O(1)$查询。
题解:
建立ST表,ST表是一种基于倍增的用于快速查询区间最值的数据结构,它由一个大小为$O(nlgn)$的二维数组构成,用$st[i][j]$表示从$j$到$j+2^{i-1}$的最值,$j$到$j+2^{i-1}$的长度为$2^i$,那么一半的长度就等于$2^{i-1}$,那么前半段的状态表示为$st[i-1][j]$。后半段的长度也为$2^{i-1}$,起始位置为$j+2^{i-1}$。那么后半段的状态表示为$st[i-1][j+2^{i-1}]$。所以:$st[i][j]=ope(st[i-1][j],st[i-1][j+2^{i-1}])$。
然后是查询:我们了解如下定理:$2^{log(a)+1}>a$,那么我们要查询$l$到$r$的最值。设$len=r-l+1,t=log(len)$,根据上面的定理:$2^t>len/2$,所以从位置上来说,$l+2^t$越过了$l$到$r$的中间!因为位置过了一半,所以$l$到$r$的最值可以表示为$ope$(从$l$往后$2^t$的最值,从$r$往前$2^t$的最值),前面的状态表示为$st[t][l]$。设后面(从$r$往前$2^t$的最值)的初始位置是$k$,那么$k+2^t-1=r$,所以$k=r-2^t+1$,所以后面的状态表示为$st[t][r-2^t+1]$,所以$l$到$r$的最小值表示为$ope(st[t][l],st[t][r-2^t+1])$,所以查询时间复杂度是$O(1)$。(参考博客:https://blog.csdn.net/Hanks_o/article/detail/77547380)
AC代码:
#include <iostream>
#include <cmath>
#include <cstdio>
int num[1000005];
int st[25][1000005];//长度为i起始位置为j的最小值
#define max(a,b) (a>b?a:b)
using namespace std;
void init(int n)
{
for(int i=1;i<=n;++i)
scanf("%d",&num[i]);
for(int i=1;i<=n;++i)
st[0][i]=num[i];
for(int i=1;(1<<i)<=n;++i)
{
for(int j=1;j<=n;++j)//j+(1<<i)是某一段开始,-1表示上一段的结束
if(j+(1<<i)-1<=n)
st[i][j]=max(st[i-1][j],st[i-1][j+(1<<(i-1))]);
}
}
int query(int l,int r)
{
int k=int(log2(r-l+1)/log2(2.0));
return max(st[k][l],st[k][r-(1<<k)+1]);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init(n);
int a,b;
for(int i=0;i<m;++i)
{
scanf("%d%d",&a,&b);
printf("%d\n",query(a,b));
}
return 0;
}