P1816 忠诚 ST表模版

寒假学了ST表之后,一直没有写相关的题目,ST表的写法都快忘了,今天复习的时候,就做了几个题,来复习一下ST表。

我不知道为什么题解里都用线段树来做,线段树不但慢,还不好写。这道题分明没有修改操作,用ST表离线处理之后用O(1)的复杂度来查询就能A了。

ST表就是一个用来解决rmq(区间最值)问题的算法。ST表不支持在线修改。预处理时间复杂度O(nlogn),查询时间O(1)。

ST表需要用到DP和倍增的思想,先建立一个二维数组st[i][j],表示从第i个元素开始的2的j次方个数的最小值。

 举个例子

1 2 3 4 5 五个数

st[2][2] 就是2 3 4 5这四个数中的最小值

st[3][0]就是3这一个数中的最小值

弄清楚st数组所代表的意义后,先把怎样求出st数组放在一边,来看一下如何利用st数组求解区间最值(RMQ)问题。


对于一个询问,会有一个左端点,一个右端点,因为st表所表示的范围是不断倍增的,不一定有元素正好覆盖查询区间,就需要用两个区间覆盖查询区间,因为是最值问题,所以两个区间有交叉部分并不会影响结果,只需取一个k,使得2的k次方小于区间长度(右端点-左端点+1),并且使k尽可能的大。

然后在st表中取两个元素,第一个元素的起始位置使区间的左端点,第二个元素的结束位置是区间的右端点,然后在在两个元素中取最小值。这个方法的正确性和实现方法都是显而易见的,结合代码都能理解,这里就不再赘述了。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,st[100001][21],ans[100001],a[100000],f1,f2,kkk;
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);    
        st[i][0]=a[i];    
    }
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i<=n-(1<<j)+1;i++)
    st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&f1,&f2);
        kkk=0;
        while((1<<(kkk+1))<=(f2-f1+1))kkk++;
        ans[i]=min(st[f1][kkk],st[f2-(1<<kkk)+1][kkk]);
    }
    for(int i=1;i<=m;i++)
    printf("%d ",ans[i]);
    return 0;
}

 

posted @ 2018-03-03 22:44  蒟蒻炖辣鸡  阅读(...)  评论(... 编辑 收藏