zoukankan      html  css  js  c++  java
  • ST表

    ST算法(Sparse Table,又称稀疏表、ST表)是用来解决RMQ问题的一种算法。

    RMQ(Range Minimum/Maximum Query)问题是询问区间最值的一种问题。

    ST表的实质是利用倍增思想的动态规划。相较于朴素的暴力(O(n))做法,ST表可以做到(O(nlog n))时间的预处理,(O(1))时间的查询,是解决这类问题最快的算法,但不支持在线修改。
    ST表利用一个二维数组记录答案(典型的以空间换时间),在这个数组中(st[i][j])表示从第(i)个数开始连续(2^j)个数中的最值(包括第(i)个数),因此查询的区间范围为([i,i+2^j-1]),并且初始条件为(st[i][0]=)(i)个数。在构造的过程中,我们有如下转移方程(以最大值为例)

    [st[i][j]=max(st[i][j-1],st[i+2^{j-1}][j-1]) ]

    需特别注意构造时的细节。第一,第(i+2^j-1)个数(右边界)不能出界(即不超过最后一个数的序号);第二,两层循环的外层应是(j),这可以感性地理解一下,因为每次构造ST表时都是先让区间长度固定将序列全部遍历完后再修改区间长度,而这里的区间长度就对应循环里的(j)
    构造说完后,再说查询。设读入区间范围为([l,r]),那么它的长度为(r-l+1),由于有可能区间的长度不能严格等于(2)的幂次,所以我们会把它分成两段分别查询然后求最值,这并不影响结果。具体做法是,先求出(k=leftlfloor {{{log }_2}(r - l + 1)} ight floor) ,然后将它分成([l,l+2^k-1])([r-2^k+1,r])两段,即在查询时分别查询(st[l][k])(st[r-2^k+1][k]),注意到这两段有重合的地方,且长度均为(2^k-1)。至于为什么这么分是正确的,是基于一个公式

    [2^{lfloor{log_2 N} floor}>frac{N}{2} ]

    因此我们取对数总能保证两段将整个区间完全覆盖。
    最后,还有一点小插曲值得一提,在我看到的求对数的写法中,基本上都是int k = (int)(log((double)(r-l+1))/log(2.0)),由于数学库中没有提供以任意底求对数的函数,于是就用了换底公式进行了转换(让我对换底公式有了深刻的记忆2333),换底公式为

    [log_aN=frac{log_bN}{log_ba} ]

    但由于这里要求(2)为底的对数,所以不用这么麻烦,直接用库里提供的log2()就好啦!


    说完了这些,刚好附一道裸的模板题的代码,就当板子用啦qwq
    (题目:[P2880]Balanced Lineup)

    #include <iostream>
    #include <cmath>
    #define MAX(a,b) a>b?a:b
    #define MIN(a,b) a<b?a:b
    using namespace std;
    int n,q,STmin[50005][20],STmax[50005][20];
    void RMQbyST()
    {
        int k=log2(n);
        for(int j=1;j<=k;j++)
            for(int i=1;i+(1<<j)-1<=n;i++)	//构造时需特别注意
                    STmax[i][j]=MAX(STmax[i][j-1],STmax[i+(1<<(j-1))][j-1]),
                    STmin[i][j]=MIN(STmin[i][j-1],STmin[i+(1<<(j-1))][j-1]);
    }
    int main()
    {
        ios::sync_with_stdio(false);
        cin >> n >> q;
        for(int i=1;i<=n;i++)
        {
            cin >> STmin[i][0];
            STmax[i][0]=STmin[i][0];	//初始条件不能忘!
        }
        RMQbyST();
        while(q--)
        {
            int l,r;
            cin >> l >> r;
            int k=log2(r-l+1);
            int Max=MAX(STmax[l][k],STmax[r-(1<<k)+1][k]),
                Min=MIN(STmin[l][k],STmin[r-(1<<k)+1][k]);
            cout << Max-Min << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    让你提升命令行效率的 Bash 快捷键
    关于jquery源码中undefined作为参数的理解
    前端手机抓包流程(window)
    前端开发--环境配置
    Code Review的人理解
    JS 组件化开发 系列(一)
    mac 安装sass时遇到gem 的镜像错误
    SpringBoot 整合mybatis
    项目知识点完善
    学习过程京东注册的静态界面
  • 原文地址:https://www.cnblogs.com/wzzyr24/p/11423512.html
Copyright © 2011-2022 走看看