学习笔记——ST表

作用:

给定一个数列 ai O(nlogn) 预处理 O(1) 查询区间最值

实现:

定义f(i,j) 为ai开始,包括ai的连续2^j个元素的最值
下面以最大值为例:
f(i,j)表示max{ak}(k∈[i,i+2^j)).
利用倍增:
f[i][j]=

{aimax(f[i][j1],f[i+2j1][j1])j=0j>0{aij=0max(f[i][j−1],f[i+2j−1][j−1])j>0


区间([L,R])求值:
把区间看做[L,L+2^j-1]和[R-2^j+1,R]两部分
虽然有重叠(或者说相交)但可以忽略(因为要求最值)
其中 j=⌊log₂(R−L+1)⌋
所以 f[i][j]=max(f[L][j],f[R-2^j+1][j]);

code:

//By Menteur_Hxy
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<ctime>
using namespace std;

const int MAX=2000;
int n,m,x,y;
int a[MAX+5],lg[MAX+5],maxn[MAX+5][MAX+5];

void solve(int *x) {
    for(int i=1;i<=n;i++) {
        lg[i]=lg[i-1];
        if(1<<lg[i-1]+1==i) lg[i]++;
        //lg[i]=((i & (i-1))==0) ? lg[i-1]+1 : lg[i-1];
    }
        for(int j=1;j<=lg[n];j++)
            for(int i=1;i+(1<<j)-1<=n;i++) 
                maxn[i][j]=max(maxn[i][j-1],maxn[i+1<<(j-1)][j-1]);

}

int askm(int l,int r) {
    int k=lg[r-l+1];
    return max(maxn[l][k],maxn[r-(1<<k)+1][k]);
}

int main() {
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++) {
        scanf("%d",&a[i]);
        maxn[i][0]=a[i];
    }
    solve(a);
    for(int i=1;i<=m;i++) {
        scanf("%d %d",&x,&y);
        printf("%d
",askm(x,y));
    }
    return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。 博主:https://www.cnblogs.com/Menteur-Hxy/
原文地址:https://www.cnblogs.com/Menteur-Hxy/p/9248019.html