1. 程式人生 > >[ST表]

[ST表]

ret xmlns name false frame %d font int sdi

RMQ問題:給定一個長度為N的區間,M個詢問,每次詢問[Li,Ri]這段區間元素的最大值/最小值。

RMQ的高級寫法一般有兩種,即為線段樹和ST表。

本文主要講解一下ST表的寫法。(以區間最大值為例)

ST表:一種利用dp思想求解區間最值的倍增算法。

定義:f(i,j)表示[i,i+2j1]這段長度為2j的區間中的最大值。

預處理:f(i,0)=ai。即[i,i]區間的最大值就是ai

狀態轉移:將[i,j]平均分成兩段,一段為[i,i+2j11],另一段為[i+2j1,i+2j1]

兩段的長度均為2j1

f(i,j)的最大值即這兩段的最大值中的最大值。

得到f(i,j)=max{f(i,j1),f(i+2j1,j1)}

void RMQ(int N){
    /*註意外部循環從j開始,
    因為初始狀態為f[i][0],
    以i為外層會有一些狀態遍歷不到。*/ 
    for(int j=1;j<=20;j++)  
        for(int i=1;i<=N;i++)
            if(i+(1<<j)-1<=N)
                f[i][j]=max(f[i][j-1
],f[i+(1<<(j-1))][j-1]); }

查詢:若需要查詢的區間為[i,j],則需要找到兩個覆蓋這個閉區間的最小冪區間。

這兩個區間可以重復,因為兩個區間是否相交對區間最值沒有影響。(如下圖)

技術分享圖片

當然求區間和就肯定不行了。這也就是RMQ的限制性。

因為區間的長度為ji+1,所以可以取k=log2(ji+1)

RMQ(A,i,j)=max{f(i,k),f(j2k+1,k)}

#include<algorithm>
#include<iostream>
#include<cstring>
#include
<cstdio> #include<cmath> using namespace std; int a[100001],f[100001][20]; inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c==-) f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-0; return x*f; } void RMQ(int N){ for(int j=1;j<=20;j++) for(int i=1;i<=N;i++) if(i+(1<<j)-1<=N) f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); } int main(){ int N=read(),M=read(); for(int i=1;i<=N;i++) a[i]=read(); for(int i=1;i<=N;i++) f[i][0]=a[i]; RMQ(N); while(M--){ int i=read(),j=read(); int k=(int)(log((double)(j-i+1))/log(2.0)); printf("%d\n",max(f[i][k],f[j-(1<<k)+1][k])); } return 0; }

[ST表]