ST表

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

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

ST表的实质是利用倍增思想的动态规划。相较于朴素的暴力\(O(n)\)做法,ST表可以做到\(O(n\log 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=\left\lfloor {{{\log }_2}(r - l + 1)} \right\rfloor\) ,然后将它分成\([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}\rfloor}>\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;
}

转载于:https://www.cnblogs.com/wzzyr24/p/11423512.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值