st表用来维护静态区间最值非常有效快捷的方法,但是只要取最值的数需要改变,那么st表便变得无用,你需要转去研究下线段树了。那么下面来看看初看不好理解,但是超好写的st表模板求最大或最小值。
问题:给出长度为N的数组,存放于data[ ]数组中,每次询问区间最大值,没有修改。
空间上需要准备一个二维数组a[i][j]用以存储以i为起点的2j个元素的最大值。如a[3][0]存储的就是以第3个元素为起点的20个元素的最大值,这个是就是data[3],那么a[3][3]存储的便是以第3个元素为起点的2^3个元素的最大值.
因此st表里初始化的时候,i为起点的2j个元素的最大值便是由上图中蓝色和黄色两个部分中最值取最大值,这两部分起点分别是i和i+2(j-1),因此便有如下的递推式:
a[i][j] = max(a[i][j-1],a[i+(1 << (j-1))][j-1]);
那么当所有的最大值记录到了a数组中后,下面便是查询最值了。假设查询的区间是[x,y],那么你需要找到那个k,让以x为起点的2k个元素和以y为终点的2k个元素的两个区间的叠加中的最大值。那么这个k的取值需要能让这样两个区间能重叠,如下图所示:
因此令k为满足2^k <= y - x +1的最大整数 ,查询[x ,y]的最值为
max(a[x][k],a[y-(1 << k)+1][k]).
上面便是对st表的初始化和查询的解释分析。下面上标程:
#include<iostream>
#define MAXN 100010
using namespace std;
int n,m;
int a[MAXN][18];
int main()
{
cin >> n >> m;
for(int i=1;i<= n; i++)
{
scanf("%d",&a[i][0]);
}
for(int j = 1;(1<<j) <= n; j++) //st表初始化
{
for(int i = 1; i + (1<<j)-1 <=n; i++)
{
a[i][j] = max(a[i][j-1],a[i+(1<<(j-1))][j-1]);
}
}
int x,y,k;
for(int i = 1; i<= m; i++)//共m条询问
{
scanf("%d%d",&x,&y);
//查询操作,查询[x,y]范围内的最大值
/*暴力求K
k = 0;
while((1<< (k+1)) <= y - x + 1 )k++;
*/
//数学公式log(a)b = log(10)b / log(10)a
k = log(y-x+1) / log(2);
printf("%d\n",max(a[x][k],a[y-(1<<k)+1][k]));
}
return 0;
}
/*建议大家做
POJ 3264 Balanced Lineup
POJ 3368 Frequent values
*/