• 【模板】st表


    今天学习了一下st表  其实好几天就一直看


    用禚神仙的话来说:

    ST表是一种用于处理静态RMQ问题(无修改区间最值问题)的最快数据结构,书写方便使用简单效率便捷。
    其中其预处理复杂度为O(nlogn),查询复杂度为O(1)。总时间复杂度为O(nlogn)。
    常数远小于树状数组、线段树等毒瘤数据结构。

    st表不支持在线修改 不支持!!!!!

    一种利用dp求解区间最值的倍增算法。

    定义:f[i][j]表示i到i+2^j-1这段区间的最大值。这里必须是i到i+2的j次方-1  别问为什么 规定!!!

    预处理:f[i][0]=a[i]。即i到i区间的最大值就是a[i]。

    状态转移:将f[i][j]平均分成两段,一段为f[i][j-1],另一段为f[i+2^(j-1)][j-1]。

    两段的长度均为2^j-1。f[i][j]的最大值即这两段的最大值中的最大值。

    得到f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1])。

     

    预处理就是运用倍增+区间动规

    ST表使用DP思想求解区间最值,貌似属于区间动态规划,不过区间在增加时,每次并不是增加一个长度,而是使用倍增的思想,每次增加2^i个长度。

    使用F[i,j]表示以i为起点,区间长度为2^j的区间最值,此时区间为[i,i + 2^j - 1]。

    比如,F[0,2]表示区间[0,3]的最值,F[2,2]表示区间[2,5]的最值。

    在求解F[i,j]时,ST算法是先对长度为2^j的区间[i,i + 2^j - 1]分成两等份,每份长度均为2^(j - 1)。之后在分别求解这两个区间的最值F[i,j - 1]和F[i + 2^(j - 1),j - 1]。,最后在结合这两个区间的最值,求出整个区间的最值。

    状态转移方程是 F[i,j] = min(F[i,j - 1],F[i + 2^(j - 1),j - 1])

    初始状态为:F[i,0] = A[i]。

     

    例题:luogu P3685 【模板】ST表

    主要变量

    1.f[i][j] : 记录给定序列中区间[i,i+pow(2,j)-1]中的最大值。
    
    起始条件:f[i][0]=a[i];
    
    (根据上面的定义,f[i][0]代表[i,i]区间最小值(即a[i]:显然区间[i,i]里只有它一个数))。
    
    2.bit[i] : bit[i]=pow(2,i)(记录2的i次方)。
    
    起始条件:bit[0]=1,之后for循环生成即可。
    
    3.a[i]:存给定序列。
    
    4.LC:在生成st表时作为f[i][j]中j的循环上界。

    ①.在f[i][j]中,i为区间左端点,j决定了f[i][j]包括的比较过的的元素个数为2^j,区间右端点下标为i+pow(2,j)-1(注意这里有个-1);

    ②.由于我们循环生成st表时按区间长度由小到大,故先循环j(1~(int)log2(n))------ (此处的j是f[i][j]中的j)。

    f[i][j]:从i出发算上i连续pow(2,j)个数的最大值

    <=>从i出发连续pow(2,j-1) 与 从i+pow(2,j-1)出发连续pow(2,j-1)这两个最大值的最大值(分成两半)

    方程:

             f[i][j]=max(f[i][j-1],f[i+pow(2,j-1)][j-1]);

    完整代码:

    #include<cstdio> 
    #include<cmath>
    #include<algorithm>
    using namespace std;
    int f[100001][40],a,x,LC,n,m,p,len,l,r;
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {   
            cin>>a;
            f[i][0]=a;
        }
        lc=(int)(log(n)/log(2));//自动向下取整(显然向上取整搞st表时比较最大值时容易把给定序列外头的元素也包括在内,不过这对于最大值么什么大碍,
    //如果是查找最小值。。。你就杯具了)
    for(int j=1;j<=lc;j++) for (int i=1;i<=n-(1<<j)+1;i++))//由于2^j为区间长度,故循环j其实就是把区间长度从1枚举到n(有可能<) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);//对于每一个区间长度枚举所有可行的左端点 for(int i=1;i<=m;i++) { cin>>l>>r; p=(int)(log(r-l+1)/log(2)); cout<<max(f[l][p],f[r-(1<<p)+1][p]); } return 0; }

    简单的模拟一下:

    给定一个数列2 4 3 5 1:
    
    此时LC=2,n=5;
    
    f[1][0]=2 f[2][0]=4 f[3][0]=3 f[4][0]=5 f[5][0]=1
    
    当j=1时, i = 1 to 4
    
    f[1][1]=max(f[1][0],f[2][0])=4;[1,2]
    
    f[2][1]=4
    
    f[3][1]=5
    
    f[4][1]=5
    
    当j=2时, i = 1 to n-bit[j]+1=2
    
    f[1][2]=max(f[1][1],f[3][1])=5;
    
    f[2][2]=5
  • 相关阅读:
    一月5日
    一月4日
    Servlet API中forward()与redirect()的区别
    get和post的区别
    java短信验证码实现
    mybatis框架
    springMVC框架
    spring框架
    redis
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/_Yrh/p/9690800.html
Copyright © 2020-2023  润新知