RMQ区间求最值

RMQ用于区间快速查找最值,适用于期间数值无更改的情况。其预处理的复杂度为O(nlogn),查询的时间复杂度为O(1),对比于线段树的预处理O(nlogn),查询O(logn)来说,在某些情况下有着其独到的优势。

RMQ原理就是在原来的数组上跑一个dp,我们以查询最大值为例,它的状态定义是这样的:

dp[ i ][ j ]:下标从i开始,长度为2^j的区间的最大值。显然dp[ i ][ 0 ]就是下标是i的那个数字本身。

下面给出其转移方程:

dp[ i ][ j ] = max( dp[ i ][ j - 1 ], dp[ i + 2 ^ j ][ j - 1 ] )

对于询问区间[ i ~ j ]的最大值Max:

设k = log( j - i + 1 ) / log( 2 )

Max = max( dp[ i ][ k ], dp[ j - 2 ^ k + 1 ][ k ] )

上述过程的具体代码如下:

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <queue>
#include <ctime>
#include <cmath>
#include <set>
#define eps 1e-10
#define MAXN 500010
#define INF 2*0x3f3f3f3f
using namespace std;

int num[MAXN], dp[MAXN][30], n, l, r;

int pow(int a, int p) { 	//求a^p这里用了快速幂,实际用应该用一个数组预处理一下
	if (p == 0) return 1;
	int ans = pow(a, p / 2);
	ans *= ans;
	if (p % 2) ans *= a;
	return ans;
}

int main() {
	//freopen("in.in", "r", stdin);
	//freopen("out.out", "w", stdout);
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &num[i]);

	for (int i = 1; i <= n; i++)	//对dp[i][0]进行初始化
		dp[i][0] = num[i];

	for (int j = 1; pow(2, j) <= n; j++)	//上文说的转移方程
		for (int i = 1; i + pow(2, j) - 1 <= n; i++)
			dp[i][j] = max(dp[i][j - 1], dp[i + pow(2, j - 1)][j - 1]);

	scanf("%d %d", &l, &r);	 //求区间[l~r]之间的最大值

	int k = log(r - l + 1) / log(2);
	int ans = max(dp[l][k], dp[r - pow(2, k) + 1][k]);
	printf("ans is : %d\n", ans);

	return 0;
}

RMQ问题在处理LCA中有着巨大的用处,其一种在线方法就是使用dfs+RMQ来求两个子节点的最近公共祖先问题,其大致做法就是按照访问的顺序把每个点的时间戳放入一个数组中,这样u和v的公共祖先就是数组中u和v之间时间戳最小的点,这里可以之间用RMQ在O(1)的时间内得到答案了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-11 23:46:11

RMQ区间求最值的相关文章

RMQ(区间求最值)

1. 概述 RMQ(Range Minimum/Maximum Query),即区间最值查询,是指这样一个问题:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j之间的最小/大值.这两个问题是在实际应用中经常遇到的问题,下面介绍一下解决这两种问题的比较高效的算法.当然,该问题也可以用线段树(也叫区间树)解决,算法复杂度为:O(N)~O(logN),这里我们暂不介绍. 2.RMQ算法 对于该问题,最容易想到的解决方案是遍历,复杂度是O(n).但当数据量

hdu4521-小明系列问题——小明序列(线段树区间求最值)

题意:求最长上升序列的长度(LIS),但是要求相邻的两个数距离至少为d,数据范围较大,普通dp肯定TLE.线段树搞之就可以了,或者优化后的nlogn的dp. 代码为  线段树解法. 1 #include <set> 2 #include <map> 3 #include <cmath> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <cct

poj3264(线段树区间求最值)

题目连接:http://poj.org/problem?id=3264 题意:给定Q(1<=Q<=200000)个数A1,A2,```,AQ,多次求任一区间Ai-Aj中最大数和最小数的差. 线段树功能:区间求最值,O(logN)复杂度查询 #pragma comment(linker,"/STACK:102400000,102400000") #include <cstdio> #include <cstring> #include <stri

区间求最值 线段树

湖南师范大学 11460 区间求最值 区间求最值   Problem description   给定一个长度为N 的数组,有q个询问,每个询问是求在数组的一段区间内那个元素的因子的个数最大,比如24的因子的个数就是8.  Input   首先是一个整数t,表示有t组测试数据,每组测试数据的第一行是一个整数N(1<=N<=10^6),第二行有N个整数ai(1<=ai<=10^6,i=1,2,.....N)表示数组的元素.第三行有一个整数q(1<=q<=10^5),代表有

HDU - 1754 I Hate It (线段树区间求最值)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1754 题意:线段树的单点更新和区间求最值 模板题,,,???,, 1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 5 typedef long long LL; 6 const int N=200010; 7 8 LL ans; 9 LL max(LL a,LL b){ 10 if(a>b) r

hdu 1754 I Hate It(树状数组区间求最值)2007省赛集训队练习赛(6)_linle专场

题意: 输入一行数字,查询第i个数到第j个数之间的最大值.可以修改其中的某个数的值. 输入: 包含多组输入数据. 每组输入首行两个整数n,m.表示共有n个数,m次操作. 接下来一行包含n个整数. 接下来m行,每行包含一个字母s,两个整数a,b. 当s为’Q’,表示查询第a个数到第b个数之间的最大值. 当s为’U’,表示将第a个数更改为b. 输出: 每次查询输出一个结果,每次输出占一行. 题解: 点修改区间求最值,可以用树状数组模板. 具体见代码—— 1 #include <cstdio> 2

湖南师范大学 11460 区间求最值

区间求最值   Problem description   给定一个长度为N 的数组,有q个询问,每个询问是求在数组的一段区间内那个元素的因子的个数最大,比如24的因子的个数就是8.  Input   首先是一个整数t,表示有t组测试数据,每组测试数据的第一行是一个整数N(1<=N<=10^6),第二行有N个整数ai(1<=ai<=10^6,i=1,2,.....N)表示数组的元素.第三行有一个整数q(1<=q<=10^5),代表有q个询问,接下来每一行有两个整数,li

[HDU] 2795 Billboard [线段树区间求最值]

Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 11861    Accepted Submission(s): 5223 Problem Description At the entrance to the university, there is a huge rectangular billboard of s

nyoj 568——RMQ with Shifts——————【线段树单点更新、区间求最值】

RMQ with Shifts 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述     In the traditional RMQ (Range Minimum Query) problem, we have a static array A. Then for each query (L, R) (L<=R), we report the minimum value among A[L], A[L+1], …, A[R]. Note that the indic