支持操作:询问区间[L,R]的最小值。
预处理操作复杂度O(nlog n),查询复杂度O(1) 。
预处理:令d(i,j)表示从i开始长度为2j的最小值,则有
d(i,j)=min( d(i,j-1) , d(i+2j-1,j-1) )
原理:
把长度为2j分成一半,则一半为 i ~ 2j-1-1,另一半为i+2j-1 ~ 2j。
预处理代码:
void RMQ_init(const vector<int>& A)
{
int n=A.size();
for(int i=0;i<n;i++) d[i][0]=A[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=0;i+(1<<j)-1<n;i++)
d[i][j]=min(d[i][j-1],d[i+(1<<(j-1))][j-1]);
}
查询操作:令k为满足2k<=R-L+1的最大整数,则以L开头,R结尾的两个长度为2k的区间和起来即覆盖了查询区间[L,R]。
(解析:第一个区间为L到L+2k,另一个区间设起点为x,则R-x+1=2k,解得x=R-2k+1,所以另一个区间为R-2k+1到R)。
有些元素重复考虑了几遍也没关系,因为是取最小值。如果是求和,则不允许出现重复考虑的元素。
查询代码:
int RMQ(int L,int R)
{
int k=0;
while((1<<(k+1))<=R-L+1) k++; //2^(k+1)<=R-L+1,那么k还可以加1
return min(d[L][k],d[R-(1<<k)+1][k]);
}