模板 ST表

ST表,適用於解決RMQ(區間最值問題),相似於線段樹和樹狀數組這兩個算法node

ST表相比於線段樹,預處理的複雜度與線段樹同樣,而查詢的複雜度則大大快於線段樹ios

  預處理 查詢
ST表  O(nlogn) O(1)
線段樹 O(nlogn) O(logn)

 

洛谷 P3865 【模板】ST表git

ST表算法

 

線段樹(不開O2):數組

 

線段樹(開O2優化): ide

 

 

經比較,咱們能夠垂手可得的看出,當咱們解決RMQ問題時,ST表的速度明顯優於線段樹函數

 

ST表的主體是一個二維數組:st[i][j]。表示查詢的數組的下標ii+2^j-1優化

如何進行預處理呢??ui

首先,咱們把從0~n-1的2^0的部分進行覆蓋spa

而後向下處理

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表
相關文章
相關標籤/搜索