[ST表]
阿新 • • 發佈:2019-01-26
ret xmlns name false frame %d font int sdi
RMQ問題:給定一個長度為N的區間,M個詢問,每次詢問[Li,Ri]這段區間元素的最大值/最小值。
RMQ的高級寫法一般有兩種,即為線段樹和ST表。
本文主要講解一下ST表的寫法。(以區間最大值為例)
ST表:一種利用dp思想求解區間最值的倍增算法。
定義:f(i,j)表示[i,i+2j−1]這段長度為2j的區間中的最大值。
預處理:f(i,0)=ai。即[i,i]區間的最大值就是ai。
狀態轉移:將[i,j]平均分成兩段,一段為[i,i+2j−1−1],另一段為[i+2j−1,i+2j−1]。
兩段的長度均為2j−1 。f(i,j)的最大值即這兩段的最大值中的最大值。
得到f(i,j)=max{f(i,j−1),f(i+2j−1,j−1)}。
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的限制性。
因為區間的長度為j−i+1,所以可以取k=log2(j−i+1)。
則RMQ(A,i,j)=max{f(i,k),f(j−2k+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表]