zoukankan      html  css  js  c++  java
  • 模板 ST表

    ST表,适用于解决RMQ(区间最值问题),类似于线段树和树状数组这两个算法

    ST表相比于线段树,预处理的复杂度与线段树一样,而查询的复杂度则大大快于线段树

      预处理 查询
    ST表  O(nlogn) O(1)
    线段树 O(nlogn) O(logn)

    洛谷 P3865 【模板】ST表

    ST表

    线段树(不开O2):

    线段树(开O2优化): 

    经比较,我们可以轻而易举的看出,当我们解决RMQ问题时,ST表的速度明显优于线段树

    ST表的主体是一个二维数组:st[i][j]。表示查询的数组的下标ii+2^j-1

    如何进行预处理呢??

    首先,我们把从0~n-1的2^0的部分进行覆盖

    然后向下处理

    Q:处理??怎么处理??

    A:不明白??那我们来举一个例子

    我们以一个长度为5的数组

    2^0部分覆盖过去就是 a[1],a[2],a[3],a[4],a[5]

    2^1部分的长度为4,也就是下标从1到4,

    st[0][1]是下标为0~1的区间的最值

    也就是st[0][0]和st[0][1]的最值

    以此往下类推

    结论:st[i][j] = min ( st[i][j-1] , st[i + 2^(j-1) ][j-1] );

    预处理函数(求最大值):

    inline void init(int n)
    {
        for(int i=1;i<=n;i++)
        {
            st[i][0]=a[i];
        }
        for(int j=1;(1<<j)<=n;j++)
        {
            for(int i=1;i+(1<<j)-1<=n;i++)
            {
                st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
            }    
        }
    }
    预处理函数

    查询函数,这个地方就很难理解了

    首先,初始化,初始化的时候,每一个状态对应的区间都是2^j

    由于查询时的长度不知道

    所以我们要用到一个定理:2^log(a)>a/2

    因为log(a)函数表示是小于等于2的最大次方

    也就是说,a>=2^(max_j)

    其中2^max_j表示的是不大于a的2的最大次方

    Q:不明白。。

    A:没关系,那我们来看几个例子就知道了

    log(4)=2; log(5~7)=2; log(8)=3; log(9~15)=3; log(16)=4; log(17~31)=4;....

    Q:可窝还是不明白。

    A:没关系,我们再以log(4)=2,log(5~7)=2为例

    这些数的log都是2

    再仔细琢磨一下log()里面的数

    4,5,6,7。floor(sqre(4)),floor(sqre(5)),floor(sqre(6)),floor(sqre(7))它们的值都等于2。

    也就是2^max<=a。

    那么我们要查询x到y的最大值

    我们设len=(y-x+1),data=log(len)=log(y-x+1)

    根据上面的定理,我们可以知道,2^data>len/2

    从下标(位置)上来看,x+2^data>(x+y)/2;

    也就是越过了区间[x,y]的中间

    因为位置超过了一半

    所以x到y的最大值就可以表示为max(从x往后的2^t的最大值,从y往前2^t的最大值);

    前面的状态我们可以表示为 st[data][x]

    设后面的初始位置为k,也就是说,从y往前2^t的最大值的初始位置是k

    那么k+2^data-1=y;

    所以k=y-2^data+1;

    所以后面的状态就是st[data][y-2^data+1]

    所以说,区间[x,y]的最大值就可以表示为max(st[data][x],st[data][y-2^t+1])

    所以查询的时间复杂度是O(1)

    查询函数:

    inline int search(int l,int r)
    {
        int k=(int)(log((double)(r-l+1))/log(2.0));
        return max(st[l][k],st[r-(1<<k)+1][k]);
    }
    查询函数

     一道ST表模板题,(上文也有提到)

    可以轻而易举的看出ST表和线段树在处理RMQ问题上的速度差异

    以下是AC代码(ST表和线段树都有 线段树的并非AC代码)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<ctype.h>
    
    using namespace std;
    
    int a[100001];
    int st[100100][21];
    
    inline int read()
    {
        int s=1, w=0; char ch=getchar();
        for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
        for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
        return s*w;
    }
    
    inline void init(int n)
    {
        for(int i=1;i<=n;i++)
        {
            st[i][0]=a[i];
        }
        for(int j=1;(1<<j)<=n;j++)
        {
            for(int i=1;i+(1<<j)-1<=n;i++)
            {
                st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
            }    
        }
    }
    
    inline int search(int l,int r)
    {
        int k=(int)(log((double)(r-l+1))/log(2.0));
        return max(st[l][k],st[r-(1<<k)+1][k]);
    }
    
    int main()
    {
        int n,m;
        n=read();
        m=read();
        for(int i=1;i<=n;i++)
        {
            a[i]=read();
        }
        init(n);
        for(int i=1;i<=m;i++)
        {
            int l,r;
            l=read();
            r=read();
            printf("%d
    ",search(l,r));
        }
        return 0;
    }
    
    //
    //#include<iostream>
    //#include<cstdio>
    //#include<cstring>
    //#include<cmath>
    //#include<ctype.h>
    //#define int long long int 
    //
    //using namespace std;
    //
    //struct node
    //{
    //    int l;
    //    int r;
    //    int w;
    //    int lazy;
    //    int mxx;
    //}tree[5000010];
    //
    //int n,m;
    //int ans;
    //int x,y;
    //
    //inline int read()
    //{
    //    int s=1, w=0; char ch=getchar();
    //    for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
    //    for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
    //    return s*w;
    //}
    //
    //void build(int l,int r,int k)
    //{
    //    tree[k].l=l;
    //    tree[k].r=r;
    //    if(tree[k].l==tree[k].r)
    //    {
    //        tree[k].w=read();
    //        tree[k].mxx=tree[k].w;
    //        return;
    //    }
    //    int m=(tree[k].l+tree[k].r)/2;
    //    build(l,m,k*2);
    //    build(m+1,r,k*2+1);
    //    tree[k].w=tree[k*2].w+tree[k*2+1].w;
    //    tree[k].mxx=max(tree[k*2].mxx,tree[k*2+1].mxx);
    //}
    //
    //inline void down(int k)
    //{
    //    tree[k*2].lazy+=tree[k].lazy;
    //    tree[k*2+1].lazy+=tree[k].lazy;
    //    tree[k*2].w+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);
    //    tree[k*2+1].w+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);
    //    tree[k].lazy=0;
    //}
    //
    //inline void ask_max_query(int k)
    //{
    //    if(tree[k].l>=x&tree[k].r<=y)
    //    {
    //        ans=max(ans,tree[k].mxx);
    //        return;
    //    }
    //    if(tree[k].lazy)
    //    {
    //        down(k);
    //    }
    //    int m=(tree[k].l+tree[k].r)/2;
    //    if(x<=m)
    //    {
    //        ask_max_query(k*2);
    //    }
    //    if(y>m)
    //    {
    //        ask_max_query(k*2+1);
    //    }
    //}
    //
    //signed main()
    //{
    //    n=read();
    //    m=read();
    //    build(1,n,1);
    //    for(int i=1;i<=m;i++)
    //    {
    //        x=read();
    //        y=read();
    //        ans=0;
    //        ask_max_query(1);
    //        cout<<ans<<endl;
    //    }
    //    return 0;
    //} 
    P3865 【模板】ST表

    2019.11.15

    考试前看了看代码,发现写的其实并不太对,线段树其实也是可以水过的

    无需O2 简而易懂

    /*
        知识点:ST表
        
        思路:ST表一开始讲的时候,没有认真听
        导致ST表不会做,然后就上网搜了一下关于ST表的博客
        基本的 
    */
    //#include<iostream>
    //#include<cstdio>
    //#include<cstring>
    //#include<cmath>
    //#include<ctype.h>
    //
    //using namespace std;
    //
    //int a[100001];
    //int st[100100][21];
    //
    //inline int read()
    //{
    //    int s=1, w=0; char ch=getchar();
    //    for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
    //    for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
    //    return s*w;
    //}
    //
    //inline void init(int n)
    //{
    //    for(int i=1;i<=n;i++)
    //    {
    //        st[i][0]=a[i];
    //    }
    //    for(int j=1;(1<<j)<=n;j++)
    //    {
    //        for(int i=1;i+(1<<j)-1<=n;i++)
    //        {
    //            st[i][j]=max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
    //        }    
    //    }
    //}
    //
    //inline int search(int l,int r)
    //{
    //    int k=(int)(log((double)(r-l+1))/log(2.0));
    //    return max(st[l][k],st[r-(1<<k)+1][k]);
    //}
    //
    //int main()
    //{
    //    int n,m;
    //    n=read();
    //    m=read();
    //    for(int i=1;i<=n;i++)
    //    {
    //        a[i]=read();
    //    }
    //    init(n);
    //    for(int i=1;i<=m;i++)
    //    {
    //        int l,r;
    //        l=read();
    //        r=read();
    //        printf("%d
    ",search(l,r));
    //    }
    //    return 0;
    //}
    
    
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<ctype.h>
    #define int long long int 
    
    using namespace std;
    
    struct node
    {
        int l;
        int r;
        int w;
        int lazy;
        int mxx;
    }tree[5000010];
    
    int n,m;
    int ans;
    int x,y;
    
    inline int read()
    {
        int s=1, w=0; char ch=getchar();
        for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
        for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
        return s*w;
    }
    
    void build(int l,int r,int k)
    {
        tree[k].l=l;
        tree[k].r=r;
        if(tree[k].l==tree[k].r)
        {
            tree[k].w=read();
            tree[k].mxx=tree[k].w;
            return;
        }
        int m=(tree[k].l+tree[k].r)/2;
        build(l,m,k*2);
        build(m+1,r,k*2+1);
        tree[k].w=tree[k*2].w+tree[k*2+1].w;
        tree[k].mxx=max(tree[k*2].mxx,tree[k*2+1].mxx);
    }
    
    inline void down(int k)
    {
        tree[k*2].lazy+=tree[k].lazy;
        tree[k*2+1].lazy+=tree[k].lazy;
        tree[k*2].w+=tree[k].lazy*(tree[k*2].r-tree[k*2].l+1);
        tree[k*2+1].w+=tree[k].lazy*(tree[k*2+1].r-tree[k*2+1].l+1);
        tree[k].lazy=0;
    }
    
    inline void ask_max_query(int k)
    {
        if(tree[k].l>=x&tree[k].r<=y)
        {
            ans=max(ans,tree[k].mxx);
            return;
        }
        if(tree[k].lazy)
        {
            down(k);
        }
        int m=(tree[k].l+tree[k].r)/2;
        if(x<=m)
        {
            ask_max_query(k*2);
        }
        if(y>m)
        {
            ask_max_query(k*2+1);
        }
    }
    
    signed main()
    {
        n=read();
        m=read();
        build(1,n,1);
        for(int i=1;i<=m;i++)
        {
            x=read();
            y=read();
            ans=0;
            ask_max_query(1);
            printf("%lld
    ",ans);
        }
        return 0;
    } 
    View Code

    祝自己 2019 CSP-S RP++!!!

  • 相关阅读:
    long类型的数据转化为时间
    取到数组中对应位置的文字,并且转成大写
    无key值的json数组解析
    mongo-2ds索引对超过半球范围的适用性测试
    mongoDB-Cannot change the size of a document in a capped collection:
    springboot
    左中右布局的五种实现方式
    spring boot 常见的配置问题
    移动端H5拍照代码实现及外网部署
    JAVA数据库操作回滚小结
  • 原文地址:https://www.cnblogs.com/Soroak/p/11656896.html
Copyright © 2011-2022 走看看