引入:
在 (RMQ) 问题(区间最值)中,有一个著名的 (ST) 算法。原理是通过倍增实现。给定一个长度 (N) 的数列,(ST) 算法在经过 (O(NogN)) 的预处理后,能够以 (O(1)) 的复杂度在线查询下标 (l o r) 之间的数的最值。
原理:
设 (F[i,j]) 表示数列 (A) 中下标在子区间 ([i,i+2^j-1]) 里的数的最值。边界条件显然是 (F[i,0]=A[i]) 。
在递推时,我们让子区间成倍增长,状态转移 (F[i,j]=max(F[i,j-1],F[i+2^{j-1},j-1])),即长度为 (2^j) 的子区间的最大值是左右两个半区间的最值中的一个。
以最小值为例:
void init()
{
for(int i=1;i<=n;i++) f[i][0]=a[i];
int t=log(n)/log(2)+1;
for(int j=1;j<t;j++)
for(int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
当询问 ([l,r]) 的最值时,先计算一个 (k),使得 (2^k<r-l+1<2^{k+1})。
那么从 (l) 开始的 (2^k) 的区间和以 (r) 为结尾的 (2^k) 的区间一定覆盖了整个 ([l,r]) 的区间。
int ask(int l,int r)
{
int k=log(r-l+1)/log(2);
return min(f[l][k],f[r-(1<<k)+1][k]);
}