• ST表


    模板-ST表

    题目背景

    这是一道ST表经典题——静态区间最大值

    请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)

    题目描述

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

    输入输出格式

    输入格式:

    第一行包含两个整数 N, M,分别表示数列的长度和询问的个数。

    第二行包含 N 个整数(记为 ai),依次表示数列的第 ii 项。

    接下来 M行,每行包含两个整数 li, ri,表示查询的区间为[li,ri]

    输出格式:

    输出包含 M行,每行一个整数,依次表示每一次询问的结果。

    输入输出样例

    输入样例#1: 复制
    8 8
    9 3 1 7 5 6 0 8
    1 6
    1 5
    2 7
    2 6
    1 8
    4 8
    3 7
    1 8
    输出样例#1: 复制
    9
    9
    7
    7
    9
    8
    7
    9

    说明

    对于30%的数据,满足: 1N,M10

    对于70%的数据,满足: 1N,M≤1e5

    对于100%的数据,满足: 1N1e5,1M1e6,ai[0,1e9],1liriN

    关于这道区间最值的题,很显然要用ST表吧。

    那么,ST表是什么呢?

    简单的说,ST表就是通过初始化的方式来求区间最值,从而达到建表复杂度为O(nlogn),查询复杂度只为O(1)!!!(不激动吗?)

    但显然它也有一定的弊端,就是建表之后就不能再更改(其实也不是不行,大不了重新建一个呗(想想时间复杂度。。。)),这也确立了它的局限性。。。

    好的那么我们该如何实现呢?

    1.建表

    先确定一件事:长度为1的区间最大值就是该数本身(显然吧)

    我们想一想,如果要记录每一个区间的最值,那么我们就需要把每一个区间都扫一遍,时间复杂度可想而知。。。

    那么我们该怎么办呢?

    这里我们用到了一种思想(也属于一种算法):倍增!!!

    在讲他之前,我们先定义一个二维数组p[j][i]表示从第i个数开始,长度为pow(2,j)的区间的最大值。

    好的,你可能又要问倍增是什么?

    倍增就是2的n次幂,其中n不断的变大,由于他的增长是2的指数级的,因此称之为倍增。

    我们来想一个问题:对于任意一个区间,他的最大值是不是就等于((它的分出的各个区间的最大值)的最大值)(好吧,有点绕)。。。

    这个是显然的吧。。。

    那么,我们是不是可以将它分为各个区间呢?(问题的关键)

    再结合倍增,我们可以把一个区间[i,i+pow(2,j)-1]表示为[i,i+pow(2,j-1)-1]和[i+pow(2,j-1),i+pow(2,j)-1];(由此我们得到了状态转移方程;dp的出现了)

    或许这张图会更明白一些。。。

    因此,只要不断进行拆分,就可以利用倍增来初始化ST表。(没听懂没有关系,请再看一遍后继续往下听)

    那么代码该如何实现呢?

    其实也比较简单,只要从j=1开始倍增(j=0上文提到初始化的方法),并保证不要越界(如果越界就会RE或者加入一些十分奇怪的数。。。),再比较两个区间的最大值即可。

    2.查询

    刚刚讲的建表其实也是为了查询做准备工作,可能大家刚刚有一个疑问:不是要求区间最大值么?你怎么保证数据给的长度就正好是2的n次幂呢?

    没事,我学的时候也有这一个疑问,因此我们还有下文qwq

    你想,我们求的只是区间最大值,而不是区间之和,是不是有一点思路了呢?

    没有也没关系,我们还是用一张图来解释一下:

    看完这张图你可能有一个疑问:这个区间不是重叠了么?

    但是这又有什么影响呢?

    由于我们知道每一个点开始2的n次幂的区间最值,所以我们只要将所求区间用这个初始值完全覆盖即可,并且我们不能超出这个区间。

    所以我们只要从两个边界点向区间里看,从大到小倍减直到找到第一个2的n次幂小于区间长度,显然它大于区间的一半,我们只要将[l,l+pow(2,k)-1]和[r-pow(2,k)+1]进行比较求出最值即可。

    最后,附上本题代码:

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<algorithm>
     4 using namespace std;
     5 int a[100005],p[50][100005];
     6 void start(int n)
     7 {
     8     int t=log2(n);
     9     for(int k=1; k<=t+1; k++)
    10     {
    11         for(int i=1; i+(1<<k)-1<=n; i++)
    12         {
    13             p[k][i]=max(p[k-1][i],p[k-1][i+(1<<(k-1))]);
    14         }
    15     }
    16 }
    17 void query(int l,int r)
    18 {
    19     int t=log2(r-l+1);
    20     printf("%d
    ",max(p[t][l],p[t][r-(1<<t)+1]));
    21 }
    22 int main()
    23 {
    24     int n,m;
    25     scanf("%d%d",&n,&m);
    26     for(int i=1; i<=n; i++)
    27     {
    28         scanf("%d",&a[i]);
    29         p[0][i]=a[i];
    30     }
    31     start(n);
    32     for(int i=1; i<=m; i++)
    33     {
    34         int l,r;
    35         scanf("%d%d",&l,&r);
    36         query(l,r);
    37     }
    38     return 0;
    39 }
  • 相关阅读:
    C# 实现 Aop [Emit动态生成代理类方式]
    分享一些最近在看的电子书
    Can't connect to your phone. Disconnect it, restart it, then try connecting again
    07,Windows Phone后台代理
    .NET 性能测试工具 性能计数器
    windows 8 metro 开发学习资源链接
    08,Windows Phone 本地存储
    06,Windows Phone 8程序的生命周期
    .NET 性能测试工具 事件跟踪器(ETW)
    LitJSONjson 和net 的完美组合用法
  • 原文地址:https://www.cnblogs.com/yufenglin/p/10305370.html
Copyright © 2020-2023  润新知