zoukankan      html  css  js  c++  java
  • ST算法

     洛谷 3865

    题目背景

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

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

    题目描述

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

    输入输出格式

    输入格式:

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

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

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

    输出格式:

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

    输入输出样例

    输入样例#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%的数据,满足: 1 leq N, M leq 101≤N,M≤10

    对于70%的数据,满足: 1 leq N, M leq {10}^51≤N,M≤105

    对于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 N1≤N≤105,1≤M≤106,ai​∈[0,109],1≤li​≤ri​≤N

    题解

    这道题目是一道st表的题目,洛谷里面已经有很详尽的解释了,但我还是要解释一波。

    f[i][j] 的意思是,以第i个为起始的元素,然后到它后面第2^j-1个元素为止,这里面的最大值。

    比如 f[1][0] 的意思就是从第一个开始,所有的数是从第一个开始放置的,到后面第 2^0-1 个元素的区间里面的最大值就是它本身,因为区间里面只有它这一个值。

    而 f[1][2] 的意思就是说要求从第一个开始,到它后面第 2^2-1=3 元素的区间里面的最小值。

    这个区间说白了就是包括第一个数,区间的长度为 2^j 的区间,因为减一就是去掉了第一个数字。

    LC求的是已知它给出数列的长度n后,求出的以2为底,以n为指数的对数,因为f里面放置的是区间长度为1到LC的不同区间的最大值。而且这些不同的区间的起始的值也是不同的。

    那怎么求呢?对于 f[i][j] 来说,我们只需要比较它的前一半区间,从i到第2^j-1再减一这个范围内的最大值,即 f[i][j-1] ,和i+(1<<(j-1))到 第2^j-1 再减一这个范围内的最大值进行比较就可以得到。

    因为区间长度为二的 J 次方,所以一半就等于二的 J-1次方,这是第二个方括号里面的内容,然后就是右区间第一个方括号里面的内容,它的意思就是在原序号的基础上加上这个整区间的一半长度,因为(1<<(j-1)) 的意思就是 2的 j-1 次方,然后不用减一,就是这样。 

    最后查询的时候,我们直接覆盖查询,也就是说,左区间的右边界可以大于右区间的左边界,半区间长度可以取成接近它本身的长度,2 ^ p -1 , p =log 2 ( r - l +1 ) 。

    还有就是要求区间的长度是不能超过该区间的总长度的,为什么要说这个呢?是因为,双重 for 循环求f[][] 数组的时候,内层i的取值范围是 : i + 2 ^ j    -1  <=n    ,因为这是要求的区间长度。

    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const int maxn = 1e6 + 9;
    int f[maxn][41],a[maxn];
    int main()
    {
        int n, m, l, LC, p, r;
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n;i++) {
            scanf("%d", &f[i][0]);
        }
        LC = (int)(log(n) / log(2));
        for (int j = 1; j <= LC;j++)
            for (int i = 1; i <= n - (1 << j) + 1;i++)
                f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
        while (m--) {
            scanf("%d%d", &l, &r);
            p = (int)(log(r - l + 1) / log(2));
            printf("%d
    ",max(f[l][p],f[r-(1<<p)+1][p]));
        }
        return 0;
    }
  • 相关阅读:
    225. 用队列实现栈
    415. 字符串相加
    rabbitmq的基本使用
    3. 无重复字符的最长子串
    面试题59
    面试题30. 包含min函数的栈
    面试题09. 用两个栈实现队列
    287. 寻找重复数
    1137. 第 N 个泰波那契数
    70. 爬楼梯
  • 原文地址:https://www.cnblogs.com/xyqxyq/p/10211383.html
Copyright © 2011-2022 走看看