ST表
- ST表的功能很简单
- 它是解决RMQ问题((Range Minimum/Maximum Query)的一种强有力的工具
- 它可以做到O(nlogn)预处理,O(1)查询最值,但是不支持修改操作。所以ST表是一种离线的数据结构
算法
ST表是利用的是倍增的思想
- 区间最大值是一个具有“可重复贡献”性质的问题。即使用来求解的预处理区间有重叠部分,只要这些区间的并是所求的区间,最终计算出的答案就是正确的。
以最大值为例,设f [ i ] [ j ] 表示整个数列A 中下标在子区间[ i , i + 2 j − 1 ] 里的数的最大值,也就是从 i 开始的 2^j 个数的最大值。递推边界显然是f [ i ] [ 0 ] = A [ i ] ,即数列A 在区间[i, i]里的最大值。
预处理
在递推时,我们把子区间的长度成倍增加,于是我们就能轻轻松松得到下面的这个递推表达式:
f [ i ] [ j ] = m a x ( f [ i ] [ j − 1 ] , f [ i + 2^ j − 1 ] [ j − 1 ] )
预处理的代码(代码中用到了对数的换底公式)注意代码中的循环顺序一定是先枚举倍增次数,才能确保递推的正确性。
int t = log(n) / log(2)+1; //最大j的范围
for (int j = 1; j <=t; 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]);
查询
当查询任意区间[ l , r ] 的最大值时,我们先计算出一个k ,满足:
区间[ l , r ] 之间的最大值就是:
先计算出k,再分别由左右区间端点为起点,加上长度分别为:
两区间长度中间有重叠部分,则并集就是区间【l,r】,取区间最大值则为查询结果。
x = read();
y = read();
double t = log(y-x+1) / log(2); //计算查询区间长度的log2的值
int k = t;
printf("%d\n", max(f[x][k], f[y-(1<<k)+1][k]));