ST表介绍:
ST表用于解决区间最大值或最小值问题,他的速度比线段树快,创建速度:O(nlogn),查询速度O(1)。在对速度要求高的地方可以选择使用ST表维护区间最值。
ST表基本原理是倍增,对一维的区间,需要一个二维数组来进行维护:
f[i][j] 表示为:从第i个元素开始,总共2^j个元素的最值
例如f[3][2]涵盖的区间如图黄色区间所示:
ST表建表:
基本原理:
ST表建立的原理是:从两个已经确定的小区间推出一个更大的区间。
可以从图中看出:f[1][2] 是由 f[1][1] 和 f[3][1] 中的最值推出的。
f[i][j]的区间长度一定是f[i][j-1]区间的两倍。所以我们可以将f[i][j]划分为两个一样长度的区间,由这两个区间的最值得出f[i][j]区间的最值。
f[ i ][ j ] 可以划分为 f[ i ][ j-1 ] 和 f[ i + 2^(j-1) ][ j-1 ]
范围确定:
在建表过程中 i 和 j 的范围该如何确定呢?
j的范围:
首先可以知道:当 j 最大的时候 i 一定是1,所以
i的范围:
当 j 确定后,i的范围也就可以确定了:
为什么要先确定j呢?
因为我们建表时是从小区间推导到大区间,要先把所有的小区间先构建完再去构建大区间,所以在二重循环中,最外层是j的循环,最内层应该是i的循环,所以我们要先确定j的范围。
代码实现:
//计算以2为底的log值(向下取整)
void LOG(int n){
log[1] = 0;
for(int i=2;i<=n;i++)
log[i] = log[i>>1] + 1;
}
//a[n]是数列,f[n][log[n]]是ST表
void build(){
for(int j=1;j<=log[n];j++)
for(int i=1;i<=(n-(1<<j)+1);i++)
f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}
ST表查询:
基本原理:
之前我们知道了ST表的原理,那么查询就很简单了。
ST表中f[i][j] 表示为:从第i个元素开始,总共2^j个元素的最值,所以一次查询大概率是不能完整包含一个任意区间的。
对于一个区间[x,y],我们只查一次ST表,这个结果能涵盖[x,y]中最大的区间长度是:,究其原因是因为我们log[y-x+1]是向下取整得出的结果。
所以我们可以通过两次查表,再对两次查表的结果取最值,就可以得出整段区间的最值。
可以看到如果要表示[1,5]这个区间,我们需要用f[1][2]和f[2][2]才能完整表示。
一定是大于等于区间长度一半的,所以我们选择两个长度为的区间就一定能涵盖[x,y]这个区间。既然长度已知,右端点已知,那么我们选择的第二个f[ i ][ log[y-x+1] ]中的 i 也就可以推导出来。
代码实现:
int find(int x,int y){
int len = log[y-x+1];
return max(f[x][len],f[y-(1<<len)+1][len])
}