RMQ问题,即区间最值问题
tarjan发明的基于倍增的st表算法
可以在
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的时间内求解静态RMQ问题
用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示序列区
[
i
,
i
+
2
j
]
[i,i+2^j]
[i,i+2j]内的最值
初始化
d
p
[
i
]
[
0
]
=
a
[
i
]
dp[i][0]=a[i]
dp[i][0]=a[i]
那么dp数组就有递推式
d
p
[
i
]
[
j
]
=
m
a
x
(
d
p
[
i
]
[
j
−
1
]
,
d
p
[
i
+
(
1
<
<
j
−
1
)
]
[
j
−
1
]
)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1])
dp[i][j]=max(dp[i][j−1],dp[i+(1<<j−1)][j−1])
即区间
[
i
,
i
+
2
j
]
[i,i+2^j]
[i,i+2j]中的最值等于区间
[
i
,
i
+
2
j
−
1
]
[i,i+2^{j-1}]
[i,i+2j−1]或
[
i
+
2
j
−
1
,
j
]
[i+2^{j-1},j]
[i+2j−1,j]中的最值
for(int i=1;i<=n;i++)
dp[i][0]=a[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
当我们查询区间
[
l
l
,
r
r
]
[ll,rr]
[ll,rr]中的最值时
可以枚举一个断点k
比较区间
[
l
l
,
l
l
+
2
k
]
[ll,ll+2^k]
[ll,ll+2k]和
[
r
r
−
2
k
+
1
,
r
r
]
[rr-2^k+1,rr]
[rr−2k+1,rr]得到答案
int k=0;
while((1<<k+1)<=rr-ll+1)k++;
ans=max(dp[ll][k],dp[rr-(1<<k)+1][k]);
虽然两个区间可能会重叠,但是这并不影响结果,因为重叠部分也属于
[
l
l
,
r
r
]
[ll,rr]
[ll,rr]
这也是用于比较的第二个区间是
[
r
r
−
2
k
+
1
,
r
r
]
[rr-2^k+1,rr]
[rr−2k+1,rr]的原因
如果第二个区间是
[
l
l
+
2
k
,
l
l
+
2
k
+
2
k
]
[ll+2^k,ll+2^k+2^k]
[ll+2k,ll+2k+2k]将有可能越出rr的边界
#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
int read()
{
int f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return x*f;
}
void print(int x)
{
if(x<0){putchar('-');x=-x;}
if(x>9) print(x/10);
putchar(x%10+'0');
}
int n,m;
int d[1000010];
int dp[1000010][55];
int main()
{
n=read();m=read();
for(int i=1;i<=n;i++)
d[i]=read();
for(int i=1;i<=n;i++)
dp[i][0]=d[i];
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
dp[i][j]=max(dp[i][j-1],dp[i+(1<<j-1)][j-1]);
while(m--)
{
int ll=read(),rr=read();
int k=0;
while((1<<k+1)<=rr-ll+1)k++;
int ans=max(dp[ll][k],dp[rr-(1<<k)+1][k]);
print(ans);printf("\n");
}
return 0;
}