【模板】ST表

给定一个长度为 (N) 的数列,和 (M) 次询问,求出每一次询问的区间([l,r])内数字的最大值。

说明
对于30%的数据,满足: (1 leq N, M leq 10 , 1≤N,M≤10)
对于70%的数据,满足: (1 leq N, M leq {10}^5 , 1≤N,M≤10^5)
对于100%的数据,满足: (1 leq N leq {10}^5, 1 leq M leq {10}^6, a_i in [0, {10}^9], 1 leq l_i leq r_i leq N 1≤N≤10^5 ,1≤M≤10^6,a i ∈[0,10^9],1≤l i ≤r i​ ≤N)

思路

1.(O(n))暴力枚举

左转右转都可以 详情见数据范围

2.线段树(O(logn))

左转右转都可以 详情见数据范围


所以呢,我们需要一个(O(1))的查询效率。

3.区间动规

记录(f(i,j))为区间([i,j])的最大值。

转移(f(i,j)=max(f(i,j−1),a[j]))

然而这需要(O(n^2))的预处理。

左转右转都可以 详情见数据范围

4.ST表

其实这是个经典的ST表模板。静态区间最值。

和LCA一样,都用到了倍增的思路。

我们令(f(i,j)) 为从(a[i])开始的,连续 (2^j)个数的最大值

于是我们有(f(i,0)=a[i])(多显然啊qwq)

于是我们还有(f(i,j)=max(f(i,j-1),f(i+2^{j-1},j-1)))

我太懒了不想证怎么办(画个图膜你一下就行了)

对于查询,根据(max) 的性质,我们可以把区间拆成两个相重叠的区间。

于是按照预处理来推一下就得到,查询区间([left,right])

(len=log2(right-left+1))
(max(f[left][len-1],f[right-(1<<(len-1))+1][len-1])))

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#define MAXN 100005
#define len lg[right-left+1]
#define scan(a) scanf("%d",&a)
#define print(a) printf("%d",a)
#define printn(a) printf("%d
",a)
#define printwn(a) printf("%d ",a)
#define endl printf("
")
using namespace std;
int n,m,a[MAXN],lg[MAXN],maxx[MAXN][25];

int main()
{
    scan(n); scan(m);
    for (int i=1;i<=n;i++)
    {
        lg[i]=lg[i/2]+1;
    }
    for (int i=1;i<=n;i++)
    {
        scan(a[i]);
    }
    for (int i=1;i<=n;i++)
    {
        maxx[i][0]=a[i];
    }
    for (int i=1;i<=lg[n];i++)
    {
        for (int j=1;j+(1<<i)-1<=n;j++)
        {
            maxx[j][i]=max(maxx[j][i-1],maxx[j+(1<<(i-1))][i-1]);
        }
    }
    int left,right;
    for (int i=1;i<=m;i++)
    {
        scan(left); scan(right);
        printn(max(maxx[left][len-1],maxx[right-(1<<(len-1))+1][len-1]));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Kan-kiz/p/10697425.html