ST表
基本概念
ST表是一个用来处理区间最值查询(RangeMaximum/Minimum Query)的离线算法。该算法分 为离线预处理O(n log n)和查询O(1)两个部分,其用到了倍增的思想。
某个区间查询问题是否适用ST表,关键在于其进行的操作是否允许区间重叠,例如max(a,b,c) = max{max(a,b),max(b,c)}就可以用ST表维护,而区间和问题则不能维护。
问题描述
给定一个长度为n的序列,有m次询问,每次给定区间[L , R],求区间内最大值。
算法思路
定义st表
我们设 st[i][j] 为从 i 开始的向后 2^j 个数中的最大值。假设这n个数存放的序列a中,根据定义 st[i][j] = max{a[k] | i <= k <= i+2^j - 1}。
查询操作
假设,我们要查询区间 [L,R] 中的最大值,由ST表定义,我们可知,我们可以查询从 x 开始,直至区间末尾的任意区间,由此特性,我们豁然开朗:以 区间[L,R] 为例,我们可以查询区间 [L,x] (x 是不超过 R 的最大数);同理,我们也可以查询区间 [x,R] (x 是不小于 L 的最小数);
由此得解
ans = max {st[L][k],st[R - (1<<k)+ 1][k]}
这里的k要取,使 L+2^k <= R 的最大值;
预处理
我们在上述证明了查询操作确实是O(1)的,所以如果我们真的能按照st表定义的那样去更新它,那 么该算法就真正的完成了。更新st表用到了动态规划的思想。
更新中仍然用到了倍增的思想,使得更新操作从O(n^2)优化到了O(n log n),初始状态st[x][0] = a[x],st[x][j] = max(st[x][j-1] , st[x + 1<<(j-1) ][j-1])。其思想和上述重叠查询类似,不再赘述。 但是要注意更新顺序,因为其中 j (第二维)才是阶段,而第一维 x 是状态,所以对于 j 的循环要 放在最外层。
模板
预处理
void init(){
Log[1] = 0;//预处理log函数
for(int i = 2;i <= n+1;i++)
Log[i] = Log[i/2]+1;
for(int i = 1;i <= n;i++)
st[i][0] = a[i];
for(int j = 1; (1<<j) <= n;j++){ //涉及到位运算多加括号!
for(int i = 1;i + (1<<(j-1)) <= n;i++){
st[i][j] = max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
询问操作
int ask(int l,int r){
int k = Log[r-l+1];
int mx = max(st[l][k],st[r-(1<<k)+1][k]);
return mx;//printf("%d %d\n",k,mx);
}