模板 - ST表

\(O(nlogn)\)预处理,\(O(1)\)查询。注意不要越界。

\(f[i][j]\) 表示 \([i,i+2^j-1]\) 的最大值。

显然, \(f[i][0]=a[i]\)

根据定义式,写出状态转移方程: \(f[i][j]=max(f[i][j-1],f[i+2^{j-1}][j-1])\)

我们可以这么理解:将区间 \([i,i+2^j-1]\) 分成相同的两部分

中点即为 \((i+(i+2^j-1))/2=i+2^{j-1}-1/2\)

所以 \([i,i+2^j-1]\) 可以分成 \([i,i+2^{j-1}-1]\)\([i+2^j,i+2^j-1]\)

对于每个询问 \([x,y]\) ,我们把它分成两部分 \(f[x][s],f[y-2^s+1][s]\)

其中 \(s=log_2(y-x+1)\) ,虽然这两个区间有重叠,但是重叠不会影响区间的最大值。


#include <bits/stdc++.h>
using namespace std;
#define ll long long

//既然使用ST表,就是要尽可能卡常
inline int read() {
    char c=getchar();
    int x=0,f=1;
    while(c<'0'||c>'9') {
        if(c=='-')
            f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9') {
        x=x*10+c-'0';
        c=getchar();
    }
    return x*f;
}

class ST_Table {
private:
    static const int MAXLOGN=17;
    static const int MAXN=100000;
    int logn[MAXN+5];
    int f[MAXN+5][MAXLOGN+1];

public:
    inline void init1() {
        logn[1]=0;
        for(int i=2; i<=MAXN; i++) {
            logn[i]=logn[i/2]+1;
        }
    }

    inline void init2(int n) {
        //或者改成从数组中复制也可以
        for(int i=1; i<=n; i++)
            f[i][0]=read();
        for(int j=1; j<=MAXLOGN; 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]);
    }

    inline int range_max(int l,int r){
        int s=logn[r-l+1];
        return max(f[l][s],f[r-(1<<s)+1][s]);
    }

}st;

int main() {
    int n=read(),m=read();
    st.init1();
    st.init2(n);
    for(int i=1; i<=m; i++) {
        int l=read(),r=read();
        printf("%d\n",st.range_max(l,r));
    }
    return 0;
}
posted @ 2019-06-02 20:25  韵意  阅读(140)  评论(0编辑  收藏  举报