RMQ学习笔记
RMQ学习笔记
前言:这个算法无论是从适配性还是长度来说都很有实力...💦
关于 RMQ
RMQ 是英文 Range Maximum/Minimum Query 的缩写,表示区间最大(最小)值。
详细信息
求 区间内的最大/最小数.
区间构造
- 本质是DP.设
为 的区间最大值.特别地, .(一个数的最大值是它本身). - 状态转移方程:
.
区间查询
- 设要查询的区间为
(包括LR) -
-
- 注意:为保证精度,请自己推导
,而不是直接使用 函数.
时间复杂度:
A. 超级记忆力
题目描述
小A同学拥有无与伦比的超级记忆力,他可以一次性记住很多数字。
为了考验一下小A同学的记忆力,王老师一次性给小A展示了
为方便老师检验小A同学的答案是否正确,请你先编程求出正确的答案。
输入
第一行两个整数
接下来一行为
接下来
数据范围:
解法:
- 板子,没什么可说的.注意:要开scanf!
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, L = 20;
int a[N], f[N][L];
int lg[N];
int n, m, x, y, k;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
lg[1] = 0;
for (int i = 2; i <= n; i++) lg[i] = lg[i / 2] + 1;
for (int j = 0; j < L; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
if (j == 0)
f[i][j] = a[i];
else
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
}
while (m--) {
scanf("%d%d", &x, &y);
k = lg[y - x + 1];
printf("%d\n", max(f[x][k], f[y - (1 << k) + 1][k]));
}
}
B. 荣耀之战
题目描述
小
游戏地图上有
小
请编程计算出,满足题意的方案中,最大的危险值最小是多少?
输入
第
接下来
数据范围
对于
- 要选出一个区间,使得他们在
的情况下 的总和最小.能够转化为: 选择 且 最小.很容易想到用双指针来解决.注意:一定要让已经过去的数出队!!!
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int L = 20;
long long a[N];
long long lg[N], f[N][L];
long long n, m, v[N], w[N];
#define value long long
value max(value a, value b) {
return a > b ? a : b;
}
value min(value a, value b) {
return a < b ? a : b;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", &v[i], &w[i]);
}
lg[1] = 0;
for (int i = 2; i <= n; i++) {
lg[i] = lg[i / 2] + 1;
}
for (int i = 0; i < L; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0)
f[j][i] = w[j];
else
f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
long long r = 0, sum = 0;
long long ans = INT_MAX;
for (int l = 1; l <= n; l++) {
while (r + 1 <= n and sum < m) {
sum += v[++r];
}
if (sum >= m) {
long long
len = lg[r - l + 1];
ans = min(ans, max(f[l][len], f[r - (1 << len) + 1][len]));
}
sum -= v[l]; // 一定要注意让已经过去的数出队!!!!
}
printf("%lld\n", ans);
}
C.异或
题目内容
有一个长度为
请问该数列中任意取一个区间
请注意,本题会发起 yes
,否则请输出 no
。
输入
第一行包含三个整数
第二行包含
接下来的
数据规模
对于
对于另外
对于
思路
- 首先,我们暴力肯定过不了.
- 题目要求给定区间内是否有一对数.
满足 - 简单推到可得:
是由上面式子转化而来的. - 我们随便列一组数据,并存储到
数组里面: - 然后没个数异或
得: - 然后在每个数的前面查找异或
后的数,如果找不到标记为 : - 如果要找的区间内对应的所有
中有大于 而且小于 的数,那就成功了.输出 否则就是 . - 这个找符合规定的数的过程可以使用RMQ.记录每个区间内的最小数然后方便之后查找,并且具有最优性.(不信可以试试不同的数据)
2856-异或
1.如果
2.对于每个数
3.因此我们只要求出
举例:
- 如何求
?
1.用 数组来存储每个数的位置,每遇到一个数 ,其位置 ,显然如果有相同的 , 将会记录最后一个 的位置;
2.因此查 的最后一个位置,直接取 ,如果该位置不存在,则输出 。注意:由于 的最大值可能是 ,因此要注意 数组要开大一些。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 20, L = 20;
int f[N][L], p[N], ls[(1 << 20) + 10], lg[N];
int n, m, l, r, t;
int main() {
scanf("%d%d%d", &n, &m, &t);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
p[i] = ls[x ^ t];
ls[x] = i;
}
lg[1] = 0;
for (int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
for (int i = 0; i < L; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0) // 一定要记得i初始值为0啊啊啊啊啊啊忘了好多遍啦
f[j][i] = p[j];
else
f[j][i] = max(f[j][i - 1], f[j + (1 << i - 1)][i - 1]);
}
}
while (m--) {
scanf("%d%d", &l, &r);
int len = lg[r - l + 1];
int maxx = max(f[l][len], f[r - (1 << len) + 1][len]);
if (maxx >= l)
puts("yes");
else
puts("no");
}
}
D.连续k个数的最值
题目描述
给定
输入
5 3
1 2 3 4 5
输出
3 1
4 2
5 3
解法
很好的板子题.跑两遍RMQ,分别记录最大最小值即可.
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, L = 20;
int f[N][L], a[N], lg[N]; // lg数组的本质就是求出两个下标之间的差值然后log2一下,所以直接开N就行
int fmi[N][L];
int n, q;
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
// 初始化lg数组
lg[1] = 0;
for (int i = 2; i <= n; i++) {
lg[i] = lg[i / 2] + 1;
}
// 初始化最大值ST表
for (int i = 0; i < L; i++) { // i从0开始!!!!2^0也是正整数!!!!
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0) // 同上个注释
f[j][i] = a[j];
else
f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
// 初始化最小值ST表
for (int i = 0; i < L; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0)
fmi[j][i] = a[j];
else
fmi[j][i] = min(fmi[j][i - 1], fmi[j + (1 << (i - 1))][i - 1]);
}
}
// 构造完毕.
for (int i = 1; i + q - 1 <= n; i++) {
int len = lg[q];
int minn = min(fmi[i][len], fmi[i + q - 1 - (1 << len) + 1][len]);
int maxx = max(f[i][len], f[i + q - 1 - (1 << len) + 1][len]);
cout << maxx << " " << minn << "\n";
}
return 0;
}
E. 体育课
题目描述
体育课上,
体育老师安排大家玩
这个游戏主要考验同学们的团队协作能力,不过,如果被邀请的同学身高差距太大,会很难完成游戏。
为了让每次邀请的同学都能顺利完成游戏,体育老师要求每次选取出编号在
输入
第
接下来
接下来读入
输入
6 3
1
7
3
4
2
5
1 5
4 6
2 2
输出
6
3
0
数据范围
解法
跟Extra T1 一摸一样.数据范围反倒小啦
#include <bits/stdc++.h>
using namespace std;
const int N = 1e4 * 5 + 10, L = 20;
int f[N][L], a[N], lg[N]; // lg数组的本质就是求出两个下标之间的差值然后log2一下,所以直接开N就行
int fmi[N][L];
int n, q, l, r;
int main() {
scanf("%d%d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
// 初始化lg数组
lg[1] = 0;
for (int i = 2; i <= n; i++) {
lg[i] = lg[i / 2] + 1;
}
// 初始化最大值ST表
for (int i = 0; i < L; i++) { // i从0开始!!!!2^0也是正整数!!!!
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0) // 同上个注释
f[j][i] = a[j];
else
f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
// 初始化最小值ST表
for (int i = 0; i < L; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0)
fmi[j][i] = a[j];
else
fmi[j][i] = min(fmi[j][i - 1], fmi[j + (1 << (i - 1))][i - 1]);
}
}
// 构造完毕.
while (q--) {
scanf("%d%d", &l, &r);
int len = lg[r - l + 1];
int maxx = max(f[l][len], f[r - (1 << len) + 1][len]);
int minn = min(fmi[l][len], fmi[r - (1 << len) + 1][len]);
printf("%d\n", maxx - minn);
}
return 0;
}
Extra T1 P2880 [USACO07JAN] Balanced Lineup G
没什么可说的,两次RMQ板子分别最大最小,还需要多打打板子题啊...好多小细节需要注意,但是大体上没什么难度.
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 * 5 + 10, L = 20;
int f[N][L], a[N], lg[N]; // lg数组的本质就是求出两个下标之间的差值然后log2一下,所以直接开N就行
int fmi[N][L];
int n, q, l, r;
int main() {
cin >> n >> q;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
// 初始化lg数组
lg[1] = 0;
for (int i = 2; i <= n; i++) {
lg[i] = lg[i / 2] + 1;
}
// 初始化最大值ST表
for (int i = 0; i < L; i++) { // i从0开始!!!!2^0也是正整数!!!!
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0) // 同上个注释
f[j][i] = a[j];
else
f[j][i] = max(f[j][i - 1], f[j + (1 << (i - 1))][i - 1]);
}
}
// 初始化最小值ST表
for (int i = 0; i < L; i++) {
for (int j = 1; j + (1 << i) - 1 <= n; j++) {
if (i == 0)
fmi[j][i] = a[j];
else
fmi[j][i] = min(fmi[j][i - 1], fmi[j + (1 << (i - 1))][i - 1]);
}
}
// 构造完毕.
while (q--) {
cin >> l >> r;
int len = lg[r - l + 1];
int maxx = max(f[l][len], f[r - (1 << len) + 1][len]);
int minn = min(fmi[l][len], fmi[r - (1 << len) + 1][len]);
cout << maxx - minn << "\n";
}
return 0;
}
ExtraT2 P3865 【模板】ST 表 && RMQ 问题
解法
就是模版好吧,不会的请看A题,注意:时间限制0.8s
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, L = 20;
int a[N], f[N][L];
int lg[N];
int n, m, x, y, k;
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
lg[1] = 0;
for (int i = 2; i <= n; i++) lg[i] = lg[i / 2] + 1;
for (int j = 0; j < L; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
if (j == 0)
f[i][j] = a[i];
else
f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
}
}
while (m--) {
scanf("%d%d", &x, &y);
k = lg[y - x + 1];
printf("%d\n", max(f[x][k], f[y - (1 << k) + 1][k]));
}
}
本文作者:FrankWKD
本文链接:https://www.cnblogs.com/FrankWKD/p/18746903
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步