模板 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\n",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\n",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\n",ans);
    }
    return 0;
} 
View Code

 

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

 

 

posted @ 2019-10-11 21:05  Soroak  阅读(443)  评论(6编辑  收藏  举报