水题链接
既然是板题了,肯定是来练模板的。
何为RMQ
RMQ实际就是一个区间找最值的东西,说道区间找最值,我们最喜爱暴力了。同时还可以用线段树这一数据结构,但,如果询问次数比较多,就爆j炸了,众所周知,线段树查询的速度是log,询问多肯定会炸,我们需要O(1)的查询速度
所以,就可以用好用的RMQ了。但不足的是,RMQ的预处理时间要比线段树大,因此我们要根据数据范围谨慎操作。
我们设dp[i][j]表示从i开始,走2^j的这一段路中的最大值。我们就需要转移。
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]);
很好理解,其实就是i点走2^j - 1的最大值与i走2^(j - 1)再走2^(j - 1)比较取最大。
解题思路
那么这道题其实没啥意思,就是一个最大,一个最小,再相减即可。
我们直接套模板可以做出来。
但其实我们虽然构造好了,但我们还要思考如何返回值。
我们定义变量k,k = log(r - l + 1)。
那么dp[l][k]就是l到l + 2^k - 1这一段区间,dp[r - (1 << k )+ 1][k]则是r - 2^k + 1 到r这一段区间。
可以证明,这一段区间肯定是完全包含l到r这一段区间的,因此就可以做出来。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<cstdlib>
using namespace std;
int n,t,a[50005],dp[50005][31],dp1[50005][31];
void make(){
for (int i = 1;i <= n;i ++){
dp[i][0] = a[i];
dp1[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]);
dp1[i][j] = min(dp1[i][j - 1],dp1[i + (1 << j - 1)][j - 1]);
}
}
}
int find(int l,int r){
int k = int (log(double(r - l + 1)) / log(2.0));
return max(dp[l][k],dp[r - (1 << k) + 1][k]) - min(dp1[l][k],dp1[r - (1 << k) + 1][k]);
}
int main(){
scanf ("%d%d",&n,&t);
for (int i = 1;i <= n;i ++)
scanf ("%d",&a[i]);
make();
while (t --){
int l,r;
scanf ("%d%d",&l,&r);
printf("%d
",find(l,r));
}
}