RMQ(Range Maximum/Minimum Query)

RMQ(Range Maximum/Minimum Query)

定义

指的是在一个序列中多次进行静态区间求最值。

算法原理

倍增思想。

实现

分为两部分:预处理和询问(S 组算法里最长的预处理)。

first part:预处理

维护 dp_{i,j}dpi,j​ 表示以下标 ii 为起点,跨度为 2^j2j 的最大值。

状态转移:dp_{i,j}=\max(dp_{i,j-1},dp_{i+2^{j-1},j-1})dpi,j​=max(dpi,j−1​,dpi+2j−1,j−1​)

初始状态:dp_{i,0}=a_idpi,0​=ai​。

维护 lg_ilgi​ 表示 ii 取以 22 为底的对数向下取整。lg_i=-1lgi​=−1,lg_i=lg_{i>>1}+1lgi​=lgi>>1​+1。

second part:询问

对于 l,rl,r 的询问,一定能找到 22 的幂超过 r-l+1r−l+1 的一半。

求覆盖区间一半以上的幂,p=lg_{r-l+1}p=lgr−l+1​。

\max(dp_{l,p},dp_{r-2^p+1,p})max(dpl,p​,dpr−2p+1,p​) 即为答案。

代码

复制代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int dp[100005][20], a[100005], n, m, lg[100005];
void init()
{
	lg[0]=-1;
	for(int i=1;i<=n;i++)
	{
		dp[i][0]=a[i];
		lg[i]=lg[i>>1]+1;
	}
	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]);
		}
	}
}
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	init();
	while(m--)
	{
		int l, r;
		cin>>l>>r;
		int mi=lg[r-l+1];
		cout<<max(dp[l][mi],dp[r-(1<<mi)+1][mi])<<"\n";
	}
}

例题

First:P2048 超级钢琴

温馨提示:本题不适合初学者阅读并尝试,同时我不会放代码(因为紫题)。

题意

给定 nn 个元素,选 kk 个不完全重复区间,区间长度在 L,RL,R 内,求 kk 次选取区间和所得的最大值。

先考虑简化版 k=1 时

维护 sum_isumi​ 表示前缀和。维护 dp_{i,j}dpi,j​ 表示以 ii 为起点跨度为 2^j2j 的区间内最大的前缀和。

循环枚举区间起点 ii,在 i+L-1,\\min(n,i+R-1)i+L−1,min(n,i+R−1) 询问最大前缀和,维护 \max(sum_i-sum_{i-1})max(sumi​−sumi−1​)

分析

现在来考虑 k \le 500000k≤500000 的情况。

考虑优先队列维护前 kk 大的区间和,问题是起点 jj 贡献的区间可能不止一个。

维护 pos_{i,j}posi,j​ 表示 dp_{i,j}dpi,j​ 在序列中所在的下标,pos_{i,0}=iposi,0​=i。

当 dp_{i,j-1} > dp_{i+2^{j-1},j-1}dpi,j−1​>dpi+2j−1,j−1​ 时,pos_{i,j}=pos_{i,j-1}posi,j​=posi,j−1​。

否则,pos_{i,j}=pos_{i+2^{j-1},j-1}posi,j​=posi+2j−1,j−1​。

对于以 ii 为起点的最大区间和,可借助 pospos 数组找到终点 jj,然后在区间 i+L-1,j-1i+L−1,j−1 和区间 j+1,i+R-1j+1,i+R−1 内再次寻找 22 个最大区间和对应的 j1,j2j1,j2,插入优先队列。

循环从优先队列中取 kk 个区间,每取 11 次,将被取出的区间终点 jj 一分为二,重复 kk 次,最后求和即为答案。

有Latex版请在安全访问中心 - 洛谷查看

相关推荐
sheeta199839 分钟前
LeetCode 补拙笔记 日期:2026.06.07 题目:128. 最长连续序列
笔记·算法·leetcode
sheeta19981 小时前
LeetCode 补拙笔记 日期:2026.06.07 题目:1. 两数之和
笔记·算法·leetcode
柒和远方2 小时前
LeetCode 452. 用最少数量的箭引爆气球 —— 区间贪心经典:排序 + 扫描一箭穿心
javascript·python·算法
Zhang~Ling3 小时前
C++ 红黑树封装:myset和mymap的底层实现
开发语言·数据结构·c++·算法
ECT-OS-JiuHuaShan3 小时前
什么是对和错?——“有针对性定义域的逻辑值的真伪”:认识论终极追问的公理化裁决
数据库·人工智能·算法·机器学习·数学建模
Merlyn103 小时前
【栈】155. 最小栈
python·算法
一个不知名程序员www4 小时前
算法学习入门---算法题DAY5
c++·算法
San813_LDD4 小时前
[量化]《虚函数调用时间复杂度完全解析:为什么是 O(1) 以及它的真实代价》
java·数据结构·算法
MartinYeung54 小时前
[论文学习]利用索引梯度优化基于优化的 LLM 越狱攻击:MAGIC 方法的深度分析与实现
人工智能·学习·算法
数据仓库搬砖人4 小时前
特征选择三剑客:前向、后向、全子集,哪种更适合你?
算法