• RMQ 解决区间查询问题


     线段树写法不管,比较灵活。这里主要讨论DP实现。

    其实单纯说RMQ解决的是区间最值查询是不准确的,只要满足一个区间的信息可以从它的覆盖区间获得(即[L,R]<=[L,r],[l,R] (l<=r) ,允许两个子区间重合)即可使用。重合不影响最值判断,所以最值查询是可以用RMQ的,其次如同区间gcd,重合区间也是不影响求值的。一样可以用RMQ。

    下面用RMQ的实现来解释上述结论:

    dp[i][j]表示以i起始,长度为2^j的线段信息。它可以划分为dp[i][j-1],dp[i+2^(j-1)][j-1],所以状态转移就出来,dp[i][j]=某种计算(dp[i][j-1],dp[i+2^(j-1)][j-1];

    查询的话,如果像线段树那样用递归分解区间求解,复杂度log级,因为是严格分离区间,所以不存在之前说的重合影响(所以线段树适用范围大啊)。而DP实现要求在O(1)时间给出答案,所以我们可以把查询区间分解为两个尽可能大的子区间(允许覆盖),这有个很简单的写法,对于区间L,R的查询,求一下k=log2(R-L+1),两个子区间为dp[L][k],dp[R-2^k+1][k];

    以区间gcd为例:

    LL lg2(LL p)//计算log2(n)
    {
        return (LL)(log(p) / log(2));
    }
    const LL len = 100005;//数据长度
    const LL bitlen = 25;//需要的位数
    LL n;
    LL dp[len][bitlen];//dp数组
    LL bit[bitlen];//预处理2^n
    void initRMQ()//初始化
    {
        for (LL j = 1; bit[j] < n; j ++)
            for (LL i = 0; i+bit[j] <= n; i++)
                dp[i][j] = gcd(dp[i][j - 1], dp[i + bit[j - 1]][j - 1]);
    }
    LL query(LL l, LL r)
    {
        LL mig = lg2(r - l + 1.0);
        return gcd(dp[l][mig], dp[r - bit[mig] + 1][mig]);
    }

    对于需要增删改的操作,显然是线段树比较合适,对于有大量查询操作的题目,RMQ可以有效降低时间(最近遇到的这类题目对时间空间要求都很高,写代码写丑一点就挂了。提醒自己多注意)。

  • 相关阅读:
    OpenGL马赛克(八)
    windows主机和ubuntu互传文件的4种方法
    noopener, noreferrer 及 nofollow 的用法
    如果要存 IP 地址,用什么数据类型比较好?
    Java8 lambda 表达式 forEach 如何提前终止?
    Dijkstra 算法求最短路径
    oceanbase 基于docker install
    PG的int整形相除
    docker 安装mysql8.0.26
    PG的并行查询生产案例
  • 原文地址:https://www.cnblogs.com/LukeStepByStep/p/7156405.html
Copyright © 2020-2023  润新知