更优视觉请见浅谈RMQ
RMQ,即Range Minimum/Maximum Query,区间最大最小问题,一般采起ST表求解,下面将结合例题讲解ios
给定一个长度为\(N\)的数列,和\(M\)次询问,求出每一次询问的区间内数字的最大值。优化
\(1 \leq N \leq {10}^5, 1 \leq M \leq 2 \times {10}^6, a_i \in [0, {10}^9], 1 \leq l_i \leq r_i \leq N\)spa
这种问题最简单的想法就是对于每一个区间遍历一次,求出最大值.net
但这样的时间复杂度是\(O(NM)\)的,明显会超时code
那么咱们能够考虑一下怎么优化blog
发现若是咱们能够预处理出每一个区间的最大值是否是就能够了get
这时候就可使用ST表io
ST表基于倍增的思想,能够作到在\(O(nlogn)\)的时间内预处理出从每一个点日后\(2^x\)的区间内的最大值模板
设\(f[i][j]\)表示从\(i\)日后\(2^j-1\)的区间内的最大值,那么转移方程为
\(f[i][j]=max\begin{cases}f[i][j-1]\\f[i+2^{j-1}][j-1] \end{cases}\)
首先这个区间能够拆成连个区间,前一半和后一半,而后再比较这两个区间内的最大值就能够了
同时由这个转移方程哦咱们能够看出要求出\(f[i][j]\),那么\(f[i][j-1]\)必需要先求出来,因此循环的时候要先循环\(j\)再循环\(i\)
那么如今处理出每一个区间的最大值,咱们就须要在\(O(1)\)的时间内求出任意区间的值
如今有一个区间\([l,r]\),咱们先求出\(x=log_2(r-l+1)\)。那么这时候整个区间就能够分红\([l,l+2^x-1]\)和\([r-2^x+1,r]\)两个区间,而后就能够根据已经预处理好的\(f\)数组在\(O(1)\)的时间里查询
那么可能就有同窗会问了,若是说分红的两个区间不能覆盖原区间呢?
也就是说,会不会有\(r-2^x+1>l+2^x-1\)的状况
假设\(r-2^x+1>l+2^x-1\)
那么\(r-l+2>2\times 2^x\)
又由于\(x=log_2(r-l+1)\),也就是\(2^x=r-l+1\)
因此\(r-l+2>2*(r-l+1)\)
即\(r-l>2*(r-l)\)
由于\(r-l>0\)
因此\(r-l>2*(r-l)\)不成立
即\(r-2^x+1>l+2^x-1\)不成立
就说明\(r-2^x+1\le l+2^x-1\)
那么分红的两个区间就必定能够覆盖原来的区间,咱们能够放心的这么拆区间啦
#include<cstdio> #include<cmath> #include<iostream> using namespace std; int n,m,l,r,x,f[100005][17]; int read() { int res=0,fh=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') fh=-1;ch=getchar();} while (ch>='0'&&ch<='9') res=res*10+(ch-'0'),ch=getchar(); return res*fh; } int main() { n=read();m=read(); for (int i=1;i<=n;++i)//f[i][0]就是从i到i+2^0-1内的最大值,就是a[i] f[i][0]=read(); for (int j=1;j<=log2(n);++j) for (int i=1;i<=n+1-(1<<j);++i) f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//预处理 for (int i=1;i<=m;++i) { l=read();r=read(); x=log2(r-l+1); printf("%d\n",max(f[l][x],f[r+1-(1<<x)][x]));//O(1)求答案 } return 0; }