ST表

st表用来维护静态区间最值非常有效快捷的方法,但是只要取最值的数需要改变,那么st表便变得无用,你需要转去研究下线段树了。那么下面来看看初看不好理解,但是超好写的st表模板求最大或最小值。
问题:给出长度为N的数组,存放于data[ ]数组中,每次询问区间最大值,没有修改。
空间上需要准备一个二维数组a[i][j]用以存储以i为起点的2^j个元素的最大值。如a[3][0]存储的就是以第3个元素为起点的2^0个元素的最大值,这个是就是data[3],那么a[3][3]存储的便是以第3个元素为起点的2^3个元素的最大值.
ST表_第1张图片
因此st表里初始化的时候,i为起点的2^j个元素的最大值便是由上图中蓝色和黄色两个部分中最值取最大值,这两部分起点分别是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为起点的2^k个元素和以y为终点的2^k个元素的两个区间的叠加中的最大值。那么这个k的取值需要能让这样两个区间能重叠,如下图所示:
ST表_第2张图片
因此令k为满足2^k <= y - x +1的最大整数 ,查询[x ,y]的最值为
max(a[x][k],a[y-(1 << k)+1][k]).
上面便是对st表的初始化和查询的解释分析。下面上标程:

#include
#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<//st表初始化
    {
        for(int i = 1; i + (1<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<1][k]));
    } 
    return 0;
}
/*建议大家做
POJ 3264    Balanced Lineup
POJ 3368    Frequent values 
*/

你可能感兴趣的:(NOIP之倍增思想)