1. 程式人生 > >ST表簡介 (洛谷P3865、洛谷P2251)

ST表簡介 (洛谷P3865、洛谷P2251)

演算法用途

Sparse Table,又稱ST表,稀疏表。運用倍增的思想,可以解決RMQ,LCA等問題。其優點是線上查詢。預處理複雜度為O(nlogn),查詢複雜度為O(1)。

演算法思想

運用倍增的思想,num[i][j]表示區間[i,i+(1<<j)]的值。然後進行預處理求出num陣列。

演算法實現

以求最大值為例

① 對於預處理,有如下轉移方程式:

num[i][j]=max(num[i][j-1],num[i+(1<<j-1)][j-1]);

這是什麼東西啊??

我們來推一遍:
其實只是把[i,i+(1<<j)]這個區間給分成兩塊,一塊是[i,i+(1<<

j-1)],另一塊是[i+(1<<j-1),i+(1<<j)](i+(1<<j)==i+(1<<j-1)+(1<<j-1))。

所以這個區間的最大值就是這兩個區間的最大值的較大者。

然後就推好啦!

② 對於查詢[x,y]之間的最大值,我們可以這樣:

int j=log2(y-x+1);
printf("%d\n",max(num[x][j],num[y-(1<<j)+1][j]));

這又是什麼東西??

我們再來推一遍:
num[x][j]表示[x,x+(1<<j)]這段區間,本來是剛好的,但是因為j是向下取整的,因此不一定取得完。取到的區間長度實際為1<<

j-1。此時我們可以從y-(1<<j-1)=y-(1<<j)+1這個點開始取(取重了也沒事),取相同的長度。然後在這兩者之間取較大者即可。

模板

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 150000
using namespace std;
int n,m;
int num[MAXN+5][18];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if
(l==r) r=(l=buf)+fread(buf,1,100000,stdin); if (l==r) return EOF; return *l++; } int _read(){ int num=0; char ch=readc(); while (ch<'0'||ch>'9') ch=readc(); while (ch>='0'&&ch<='9'){ num=num*10+ch-48; ch=readc(); } return num; } void make(){ for (int i=1;i<18;i++) for (int j=1;j+(1<<i)-1<=n;j++) num[j][i]=max(num[j][i-1],num[j+(1<<i-1)][i-1]); } int main(){ n=_read(); m=_read(); for (int i=1;i<=n;i++) num[i][0]=_read(); make(); for (int i=1;i<=m;i++){ int x=_read(),y=_read(); int j=log2(y-x+1); printf("%d\n",max(num[x][j],num[y-(1<<j)+1][j])); } return 0; }
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 1500000
using namespace std;
int num[MAXN+5][21],q[MAXN+5];
int n,m;
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF;
    return *l++;
}
int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
    return num;
}
void make(){
    for (int j=1;j<=20;j++)
        for (int i=1;i<=n;i++)
            num[i][j]=min(num[i][j-1],num[i+(1<<j-1)][j-1]);
}
int main(){
    n=_read(); m=_read();
    for (int i=1;i<=n;i++)
        num[i][0]=_read();
    make();
    for (int i=1;i<=n-m+1;i++){
        int x=i,y=m+i-1;
        int j=log2(y-x+1);
        q[i]=min(num[x][j],num[y-(1<<j)+1][j]);
    }
    for (int i=1;i<=n-m+1;i++)
        printf("%d\n",q[i]);
    return 0;
}