淺談RMQ

更優視覺請見淺談RMQ
RMQ,即Range Minimum/Maximum Query,區間最大最小問題,一般採起ST表求解,下面將結合例題講解ios

例題

P3865 模板【ST表】數組

題目描述

給定一個長度爲\(N\)的數列,和\(M\)次詢問,求出每一次詢問的區間內數字的最大值。優化

\(1 \leq N \leq {10}^5, 1 \leq M \leq 2 \times {10}^6, a_i \in [0, {10}^9], 1 \leq l_i \leq r_i \leq N\)spa


這種問題最簡單的想法就是對於每一個區間遍歷一次,求出最大值.net

但這樣的時間複雜度是\(O(NM)\)的,明顯會超時code

那麼咱們能夠考慮一下怎麼優化blog

發現若是咱們能夠預處理出每一個區間的最大值是否是就能夠了get

這時候就可使用ST表io

ST表

ST表基於倍增的思想,能夠作到在\(O(nlogn)\)的時間內預處理出從每一個點日後\(2^x\)的區間內的最大值模板

\(f[i][j]\)表示從\(i\)日後\(2^j-1\)的區間內的最大值,那麼轉移方程爲

\(f[i][j]=max\begin{cases}f[i][j-1]\\f[i+2^{j-1}][j-1] \end{cases}\)

首先這個區間能夠拆成連個區間,前一半和後一半,而後再比較這兩個區間內的最大值就能夠了

同時由這個轉移方程哦咱們能夠看出要求出\(f[i][j]\),那麼\(f[i][j-1]\)必需要先求出來,因此循環的時候要先循環\(j\)再循環\(i\)

那麼如今處理出每一個區間的最大值,咱們就須要在\(O(1)\)的時間內求出任意區間的值

如今有一個區間\([l,r]\),咱們先求出\(x=log_2(r-l+1)\)。那麼這時候整個區間就能夠分紅\([l,l+2^x-1]\)\([r-2^x+1,r]\)兩個區間,而後就能夠根據已經預處理好的\(f\)數組在\(O(1)\)的時間裏查詢

那麼可能就有同窗會問了,若是說分紅的兩個區間不能覆蓋原區間呢?

也就是說,會不會有\(r-2^x+1>l+2^x-1\)的狀況

假設\(r-2^x+1>l+2^x-1\)

那麼\(r-l+2>2\times 2^x\)

又由於\(x=log_2(r-l+1)\),也就是\(2^x=r-l+1\)

因此\(r-l+2>2*(r-l+1)\)

\(r-l>2*(r-l)\)

由於\(r-l>0\)

因此\(r-l>2*(r-l)\)不成立

\(r-2^x+1>l+2^x-1\)不成立

就說明\(r-2^x+1\le l+2^x-1\)

那麼分紅的兩個區間就必定能夠覆蓋原來的區間,咱們能夠放心的這麼拆區間啦

例題Code

#include<cstdio>
#include<cmath>
#include<iostream>
using namespace std;
int n,m,l,r,x,f[100005][17];
int read()
{
	int res=0,fh=1;char ch=getchar();
	while (ch<'0'||ch>'9') {if (ch=='-') fh=-1;ch=getchar();}
	while (ch>='0'&&ch<='9') res=res*10+(ch-'0'),ch=getchar();
	return res*fh;
}
int main()
{
	n=read();m=read();
	for (int i=1;i<=n;++i)//f[i][0]就是從i到i+2^0-1內的最大值,就是a[i]
		f[i][0]=read();
	for (int j=1;j<=log2(n);++j)
		for (int i=1;i<=n+1-(1<<j);++i)
			f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//預處理
	for (int i=1;i<=m;++i)
	{
		l=read();r=read();
		x=log2(r-l+1);
		printf("%d\n",max(f[l][x],f[r+1-(1<<x)][x]));//O(1)求答案
	}
	return 0;
}
相關文章
相關標籤/搜索