[RMQ]板题

水题链接

既然是板题了,肯定是来练模板的。

何为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\n",find(l,r));
	}
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值