目錄ios
通過了樹狀數組的折磨學習,小蒟蒻\(hqk\)又學了一個新的結構——\(ST\)表(其實應該是\(ST\)算法,由於\(T\)原本就有\(table\)的意思,可是因爲你們都這麼叫,接下來的文章裏也沿用這一說法。算法
區間\(RMQ\)($ Range\space Maximum/minimum\space Query \()問題,顧名思義,就是詢問某個區間的最大最小值。這種問題一般有不少種解法,好比線段樹、樹狀數組,還有像笛卡爾樹、莫隊這樣的神仙作法。可是咱們今天要探討的解法是一種比較好理解的算法——\)ST$表數組
\(ST\)表是基於動態規劃的一種算法。爲啥叫表呢?是由於\(ST\)表在運行過程當中是先進行預處理,而後進行查詢。這種算法可以實現\(O(nlogn)\)預處理,\(O(1)\)查詢。是一種比較高效的算法,可是須要注意的一點是,這種算法的空間複雜度較高,須要一些優化(或者使用其餘的算法來代替)才能經過一些毒瘤題目函數
其實,\(ST\)表的基本思想就是\(dp\)。學習
咱們用\(a[1···n]\)表示一組數。設\(f[i][j]\)表示從\(a[i]\)加到\(a[i+2^i-1]\)這個範圍內的最大值,也就是說\(f[i][j]\)表示以\(a[i]\)爲起點連續\(2^i\)個數的最大值。因爲元素個數爲\(2^j\)個,因此咱們能夠考慮分治的思想,分而治之,分別求出左半邊(\(2^{j-1}\))的最大值,再求右半邊的最大值,即\(f[i][j]=max(f[i][j-1],f[i+2^{j-1}][j-1])\)從前日後掃描一下就能夠預處理出來。大數據
接下來咱們要考慮如何進行查詢優化
每提問一個區間\([l,r]\),必定會存在一個數\(x\),使得\(2^x\leq r-l+1\)。只要求出了這個值,咱們就能夠用已經與處理完畢的\(f[][]\)來進行回答了。spa
具體方法是:\(min(f[l][x],f[r-2^x+1][x])\)這個東西能夠再\(O(1)\)的時間內求出來code
等等,怎麼求\(x\)呢?**get
其實,求\(x\)的方法也很簡單,就是\(log_2^{r-l+1}\),具體是爲何須要讀者本身去思考,這裏就再也不贅述了
可是怎麼求\(log_2^{r-l+1}\)呢?
咱們能夠維護一個\(log[]\),其中\(log[i]\)表示\(log_2^i\)。至於\(log[i]\)的計算,咱們能夠用下面一個遞推式:\(log[i]=log[i/2]+1\);不過若是再懶一點的話能夠調用\(cmath\)庫裏的\(log2\)函數
其實就是\(ST\)表的實現
這是一道\(ST\)表經典題——靜態區間最大值
請注意最大數據時限只有\(0.8s\),數據強度不低,請務必保證你的每次查詢複雜度爲$ O(1)$
給定一個長度爲$ N \(的數列,和\) M $次詢問,求出每一次詢問的區間內數字的最大值。
第一行包含兩個整數$ N, M$,分別表示數列的長度和詢問的個數。
第二行包含\(N\)個整數(記爲 \(a_i\)),依次表示數列的第$ i$項。
接下來$ M$行,每行包含兩個整數 \(l_i, r_i\),表示查詢的區間爲$ l_i, r_i$
輸出包含$ M$行,每行一個整數,依次表示每一次詢問的結果。
這就是一道板子題啊!莫慌莫慌,都在代碼裏了——
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int N=1e6+5,logn=20; int log[N],f[N][logn+5],a[N]; int n,m,x,y; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); log[0]=-1;//必定要注意這個小細節,才能使log[1]=0 for(int i=1;i<=n;++i) { f[i][0]=a[i];//將形如[i,i]的都標做a[i],做爲dp的邊界條件 log[i]=log[i>>1]+1;//對log的處理 } for(int j=1;j<=logn;++j)//外循環是1~logn for(int i=1;i+(1<<j)-1<=n;++i)//內循環是一直到出界 f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//套進剛纔的式子中 while(m--)//循環讀入數據 { scanf("%d%d",&x,&y); int s=log[y-x+1]; printf("%d\n",max(f[x][s],f[y-(1<<s)+1][s]));//這些式子咱們都進行過說明,這裏就不說了 } return 0; }
無
爲了檢測生產流水線上總共\(N\)件產品的質量,咱們首先給每一件產品打一個分數\(A_i\)表示其品質,而後統計前M件產品中質量最差的產品的分值\(Q_m = min(A_1, A_2, ... A_m)\),以及第\(2\)至第\(M + 1\)件的\(Q_{m + 1}, Q_{m + 2} ...\) 最後統計第\(N - M + 1\)至第\(N\)件的\(Q_n\)。根據\(Q\)再作進一步評估。
請你儘快求出\(Q\)序列。
輸入共兩行。
第一行共兩個數\(N、M\),由空格隔開。含義如前述。
第二行共\(N\)個數,表示\(N\)件產品的質量。
輸出共\(N - M + 1\)行。
第\(1\)至\(N - M + 1\)行每行一個數,第\(i\)行的數\(Q_{i + M - 1}\)。含義如前述。
其實這道題也算一道模板題,只不過是將詢問變成了讓你本身循環跑一邊,不過要注意的是,必定要將\(m-1\)後再進行循環
#include<cstdio> #include<iostream> using namespace std; const int maxn=2000010; const int logn=20; int log[maxn],f[maxn][logn],a[maxn]; int n,m; void init() { scanf("%d%d",&n,&m); log[0]=-1; for(int i=1;i<=n;++i) { scanf("%d",&a[i]); f[i][0]=a[i]; log[i]=log[i>>1]+1; } for(int j=1;j<=logn;++j) for(int i=1;i+(1<<j)-1<=n;++i) f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]); } void work() { m-=1; for(int i=1;i+m<=n;++i) { int s=log[m+1]; printf("%d\n",min(f[i][s],f[i+m-(1<<s)+1][s])); } } int main() { init(); work(); return 0; }