RMQ(Range Minimum/Maximum Query)
作用:在一个给定的序列A中求区间 [ x , y ] 的最值
设序列长度为n,有m次询问
优点:预处理O(nlogn),询问O(1),总复杂度为O(nlogn+m),与线段树和树状数组相比而言,RMQ在处理询问次数多的情况下有绝对优势
思想:递推/动态规划/倍增
预处理:
首先我们预处理出数组 f [ i ] [ j ] ,代表从第 i 位开始的 2j 位的最大值,即 [ i , i + 2j -1 ] 。
如 f [ 1 ] [ 1 ] 表示从 1~2 ,f [ 2 ] [ 3 ]表示从 2~9
那么将 j 作为阶段 ,[ i , i + 2j -1 ] 可以由 [ i , i + 2j-1 - 1] 和 [ i + 2j-1 , i + 2j - 1] 两个区间得到
可以表示为如下的递推式。
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
因为是 f [ i ] [ j ] 从 j - 1 得来,所以将 j 设为阶段,套在外层循环
其中第一个 for 的范围根据题目范围设定 , 本模板的数据范围参考 [洛谷 P3865 【模板】ST表]
for(int j=1;j<=21;++j) for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
考虑到 f [ i ] [ 0 ] 为第 i 位 本身的数,所以可以将原序列的数读入到 f [ i ] [ 0 ]中
查问:
把 [ x , y ] 区间的长度记为 len = y - x + 1 , 记 k=log2(len)
那么[ x , y ] 区间的最大值为区间 [ x , x + 2k - 1] 和 [ y - 2k +1 , y] 的最大值,可得如下
int query(int x,int y){ int k=log2(y-x+1); return max(f[x][k],f[y-(1<<k)+1][k]); }
我们知道 log2 是向下取整 因此 保证以上两个小区间不超大区间的范围
因为一个小区间是从左边头开始,一个小区间是从右边头开始,小区间长度相等,小区间长度记为len2,即使log2 向下取整,两小区间仍重叠,
即 len2 = 2log2(len) ,log2 向下取整, 2log2(len) > len/2 即 2*len2>len
那么就可以保证query函数的正确性了
贴一下代码,模板题是[洛谷 P3865 【模板】ST表]
#include<bits/stdc++.h> #define maxn 100010 using namespace std; int f[maxn][22],n,m; int query(int x,int y){ int k=log2(y-x+1); return max(f[x][k],f[y-(1<<k)+1][k]); } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&f[i][0]); for(int j=1;j<=21;++j) for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); for(int i=1;i<=m;++i){ int x,y; scanf("%d%d",&x,&y); printf("%d\n",query(x,y)); } }
还有,这是绝对AC不了的,因为没加快读,会TLE的
原文:https://www.cnblogs.com/lamkip/p/13271552.html