RMQ总结

〇、注意

本文中的 log ⁡ \log log 都是以 2 2 2 为底。

一、使用范围

RMQ 是处理区间最值的一种高效算法。

二、算法描述

我们以 数列区间最大值 为例。

很容易想到,直接枚举 [ l , r ] [l,r] [l,r] 求出最大值,时间复杂度为 O ( N M ) O(NM) O(NM)

RMQ 是怎么处理的呢?定义 d p i , j dp_{i,j} dpi,j 表示从 i i i 出发,长度为 2 j 2^j 2j 的序列最大值。

  1. 初始化
    刚开始时, d p i , 0 = a i ( 1 ≤ i ≤ N ) dp_{i,0}=a_i(1 \le i \le N) dpi,0=ai(1iN)。因为 2 0 = 1 2^0 = 1 20=1,就是每个元素它自己,肯定是它自己最大。

  2. 计算
    对于任意 d p i , j ( i ≠ j 且 i < j ) dp_{i,j}(i \neq j 且 i<j) dpi,j(i=ji<j),把它看作长度均为 2 j − 1 2^{j-1} 2j1 [ i , i + 2 j − 1 − 1 ] [i,i+2^{j-1} - 1] [i,i+2j11] [ i + 2 j − 1 , i + 2 j − 1 ] [i+2^{j-1},i+2^j-1] [i+2j1,i+2j1] 两部分合并取最大值。所以 d p i , j = max ⁡ ( d p i , j − 1 , d p i + 2 j − 1 , j − 1 ) dp_{i,j}=\max(dp_{i,j-1},dp_{i+2^{j-1},j-1}) dpi,j=max(dpi,j1,dpi+2j1,j1)。其中 j j j 是作为转移的阶段,因为 j j j j − 1 j -1 j1 转移而来,所以要放在第一层循环。
    让我们来计算时间复杂度。首先 j j j 的上限是 log ⁡ N \log N logN,里面 i i i 1 1 1 N − 2 j + 1 N - 2^{j} + 1 N2j+1,约为 N N N 次。所以,预处理的时间复杂度为 O ( N log ⁡ N ) O(N\log N) O(NlogN)

  3. 查询方法
    对于查询区间 [ l , r ] [l,r] [l,r],我们的答案就是 max ⁡ ( d p l , k , d p r − 2 k + 1 , k ) \max(dp_{l,k},dp_{r-2^k+1,k}) max(dpl,k,dpr2k+1,k) ,其中 k = log ⁡ ( r − l + 1 ) k=\log(r-l+1) k=log(rl+1)。因为 d p l , k dp_{l,k} dpl,k [ l , l + 2 k − 1 ] [l,l+2^k-1] [l,l+2k1] d p r − 2 k + 1 , k dp_{r-2^k+1,k} dpr2k+1,k [ r − 2 k + 1 , r ] [r-2^k+1,r] [r2k+1,r],只需证明 l + 2 k − 1 ≥ r − 2 k + 1 l+2^k-1 \geq r-2^k+1 l+2k1r2k+1 即可。这样每个元素都参与了计算。证明:
    设 l + 2 k − 1 ≥ r − 2 k + 1 成立 设 l+2^k-1 \geq r-2^k+1 成立 l+2k1r2k+1成立
    ∴ 2 × 2 k ≥ r − l + 2 \therefore 2\times 2^{k} \geq r-l+2 2×2krl+2
    将 k = log ⁡ ( r − l + 1 ) 代入得: 将k=\log(r-l+1)代入得: k=log(rl+1)代入得:
    2 ( r − 1 + 1 ) ≥ r − l + 2 2(r-1+1)\ge r-l+2 2(r1+1)rl+2
    ∴ r − l ≥ 0 \therefore r-l\ge 0 rl0
    ∴ r ≥ l \therefore r\ge l rl
    这是我们默认的。所以假设成立。 这是我们默认的。所以假设成立。 这是我们默认的。所以假设成立。
    得证。 得证。 得证。
    这样,我们就只用 O ( 1 ) O(1) O(1) 的时间复杂度就可以进行查询了。

总的时间复杂度就是 O ( N log ⁡ N + M ) O(N\log N+M) O(NlogN+M)

参考代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int Maxn = 1e5 + 5;
int n, m, x, y, a[Maxn], dp[Maxn][20];
int main() {
	scanf("%d %d", &n, &m);
	for(int i = 1;i <= n; ++i) scanf("%d", &a[i]);
	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]);
	while(m--) {
		scanf("%d %d", &x, &y);
		int k = log2(y - x + 1);
		printf("%d\n", max(dp[x][k], dp[y - (1 << k) + 1][k]));
	}
	return 0; 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值