RMQ問題 及st表
阿新 • • 發佈:2018-11-25
rmq問題是多次詢問閉區間內的最值,時間複雜度達到O(nlogn+m)
st表是一種不支援線上修改演算法,專門解決rmq
下面是st表的詳情
st[i][j]表示在i - ((2^j)-1)這個區間之內的最值
st[i][0]就等於a[i],不用說了
既然這樣,那麼怎麼求出st的其他值呢?
st表O(nlogn)的預處理其實像極了LCA的初始化
因此st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
怎麼樣,是不是極為相似
那如果要查詢怎麼辦呢,要查詢的區間可不滿足剛好是2的若干次方哦?
大家應該都知道,st表的單次查詢時間是O(1),所以我們要用常數的查詢方法(這不是廢話嗎,其實這就是廢話)
先設t為log(y-x+1),也就是2^t<=(y-x+1) && 2^(t+1)>(y-x+1)
那麼就可用min(st[x][t],st[y-(1<<t)-1][t])完美的覆蓋了這個區間了
因此st表不支援線上修改,很明顯,這個演算法難以修改,如果要修改最好用線段樹
給一個例題吧
洛谷P1816 忠誠
題目描述
老管家是一個聰明能幹的人。他為財主工作了整整10年,財主為了讓自已賬目更加清楚。要求管家每天記k次賬,由於管家聰明能幹,因而管家總是讓財主十分滿意。但是由於一些人的挑撥,財主還是對管家產生了懷疑。於是他決定用一種特別的方法來判斷管家的忠誠,他把每次的賬目按1,2,3…編號,然後不定時的問管家問題,問題是這樣的:在a到b號賬中最少的一筆是多少?為了讓管家沒時間作假他總是一次問多個問題。
輸入輸出格式
輸入格式:
輸入中第一行有兩個數m,n表示有m(m<=100000)筆賬,n表示有n個問題,n<=100000。
第二行為m個數,分別是賬目的錢數
後面n行分別是n個問題,每行有2個數字說明開始結束的賬目編號。
輸出格式:
輸出檔案中為每個問題的答案。具體檢視樣例。
輸入輸出樣例
輸入
10 3 1 2 3 4 5 6 7 8 9 10 2 7 3 9 1 10
輸出
2 3 1
題解:
閉區間多次求最值(rmq)
程式碼:
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<cstdlib> #include<queue> #include<stack> #include<cmath> #include<map> using namespace std; inline int read(){ int x=0,f=0;char s=getchar(); while(!isdigit(s))f|=s=='-',s=getchar(); while( isdigit(s))x=(x<<1)+(x<<3)+s-48,s=getchar(); return !f?x:-x; } const int N=1e6+10; const int logn=20; int n,k; int Log[N],st[N][logn+5],a[N]; int main(){ int x,y,t; n=read();k=read(); for(int i=1;i<=n;i++)a[i]=read(); Log[0]=-1;//log[i]的意義:2^log[i]<=i && 2^(log[i]+1)>i memset(st,63,sizeof(st));//初始化無窮大 for(int i=1;i<=n;i++)st[i][0]=a[i],Log[i]=Log[i>>1]+1;//初始化log和st[i][0] for(int j=1;j<=logn;j++)//nlogn預處理出每一項st for(int i=1;i+(1<<j)-1<=n;i++) st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]); while(k--){//查詢 x=read(),y=read(); t=Log[y-x+1]; printf("%d ",min(st[x][t],st[y-(1<<t)+1][t])); } return 0; }
以上就是rmq演算法,我覺得畫圖似乎沒有什麼必要,謝謝大家的支援