RMQ1

Posted robin20050901

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RMQ1相关的知识,希望对你有一定的参考价值。

RMQ这种算法,有好处也有坏处。

好处是代码量比其他算法(线段树,树状数组等)稍短(又是很短),坏处是局限性太大,不如线段树灵活。

它的目的是求区间最值。

我们先看一道简单题。

有一个序列,以及一些操作,每次操作给出一个区间[l,r],求这个区间的最大值。

如果你之前阅读技术图片

 

或其他有关线段树的资料,这题就是小菜一碟。

当我们摆脱线段树,从另一个视角看这题。

我们把它想成一道区间DP,用dp[i][j]表示区间[i,j]的最大值。

显而易见,有dp[i][j]=max(dp[i][j-1],a[i])(时间复杂度O(n^2))

我们采用倍增思想,用dp[i][j]表示以i为起点,长度为2j的区间最大值,求解时分成两个区间[i,i+2j-1-1],[i+2j-1,i+2j+1]

有dp[i][j]=max(dp[i][j-1],dp[i+2j-1][j-1])

由于长度为2j,时间复杂度为O(nlogn)

以上是预处理部分。

实际查询时,我们需要求解一个任意的区间,而上面的预处理只能求出长度为2j的区间最大值。

我们利用一下一个小性质:
技术图片

我们的目标就是要把待查询的区间,转化成两个我们已经求出的区间。

找出最大的2k<=len的k,其中len=r-l+1为区间长度,取区间[l,l+2k-1],[r-2k+1,r]为两个小区间,显然满足且能找到,时间复杂度为O(1)

模板题代码:

#include<bits/stdc++.h>

#define rd(x) x=read()
#define N 100005 

using namespace std;

int n,m;
int a[N][25];

inline int read()
{
    int f=1,x=0;char s=getchar();
    while(s<0||s>9){if(s==-)f=-1;s=getchar();}
    while(s>=0&&s<=9){x=x*10+s-0;s=getchar();}
    return x*f;
}

int query(int l,int r)
{
    int k=log2(r-l+1);
    return max(a[l][k],a[r-(1<<k)+1][k]);
}

int main()
{
    rd(n);
    for(int i=1;i<=n;i++)rd(a[i][0]);
    for(int j=1;j<=21;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            a[i][j]=max(a[i][j-1],a[i+(1<<(j-1))][j-1]);
    rd(m);
    while(m--)
    {
        int l,r;
        rd(l),rd(r);
        printf("%d
",query(l,r));
    }
    
    return 0;
}

 

是不是感觉RMQ很方便呢,RMQ还有许许多多的拓展,下期再见!

 



以上是关于RMQ1的主要内容,如果未能解决你的问题,请参考以下文章

第六场C

微信小程序代码片段

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板