网址: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$的中间!因为位置过了一半,所以x到y的最值可以表示为$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; }