专栏文章
浅谈——RMQ问题
算法·理论参与者 8已保存评论 13
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 13 条
- 当前快照
- 2 份
- 快照标识符
- @mk9zdf97
- 此快照首次捕获于
- 2026/01/12 01:01 上个月
- 此快照最后确认于
- 2026/02/19 01:20 12 小时前
是 的缩写,表示区间最大最小值。
step1.倍增法(ST 表)
最朴素的方法。时间复杂度为 。
预处理时间复杂度 ,每次查询 。
以求最大值为例。
定义
定义 表示区间 的区间最大值。
初始化
表示区间 的结果,即 。故有 。
转移
由于区间最大值支持合并,则显然区间 可以由 和 合并而来。。回归定义,可以得出转移方程:
预处理复杂度为 。
CPPfor(int j=1;j<=log2(n);j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
询问
目前已经求出了 数组。
询问区间 的最大值。考虑用 去覆盖应有区间。
显然我们可以令 。
那么答案就是 。
思考一下,其实就是覆盖左边和右边。中间有重复没关系。全部覆盖就行。
CPPint k=log2(r-l+1);
printf("%d\n",max(f[l][k],f[r-(1<<k)+1][k]));
每次询问复杂度为 。
优点:易懂。
step2.离线单调栈
复杂度 。
排序复杂度 ,每个询问二分 。
方法
离线下来所有询问,按右端点 排序。然后每次在序列 上找到当前询问的右端点 处,并把找到的元素压入到单调栈中。这样,对于每个询问时,单调栈中存储的值满足在 中的位置 。只需找到序列中第一个 的值。显然可以二分来找。
优点:空间复杂度 。
时间复杂度 。
CPPsort(query+1,query+m+1,cmp);
for(int i=1,j=1;i<=m;i++){
while(j<=query[i].r){
while(cnt&&a[j]>a[st[cnt]]) cnt--;
st[++cnt]=j++;
}
int pos=lower_bound(st+1,st+cnt+1,query[i].l)-st;
ans[query[i].id]=a[st[pos]];
}
step3.线段树/树状数组
很模板的线段树。维护区间最大值。
建树 ,每次查询 ,总共为 。
优点:模板且没有思维含量。
那么树状数组也能做。
似乎没必要。
step4.四毛子前置算法(分块)
四毛子算法原名 。这里分析前置算法——分块。
方法
考虑将原序列每 个分进一个块,则共有 个块。
每次询问情况可分为以下几种:
- 块内:直接遍历找最大值。
- 相邻块中:前面块的一段后缀的贡献和后面块的一段前缀的贡献取并。
- 间隔很多块:考虑第一个块后缀和最后一个块的前缀,中间的整块直接用 ST 表 查询。
梳理一下就是:
- 对于每一个块预处理前缀最大值和后缀最大值。复杂度 。
- 对于每个块总体贡献建立 ST 表,那么也就是 个块之间的 ST 表,时间复杂度为 。
再发掘一下,发现每次询问最坏情况为同块时,需暴力扫一遍,则时间复杂度为 。其余情况直接可以 解决。考虑如何优化?
优化 :块内 ST 表
我们是否可以在每个块内建立 ST 表呢?
答案显然,故时间复杂度为 。
考虑取 ,得到时空复杂度都近似为 。
优点:实现简单。
优化 : 问题
继续优化同块的情况。
oi_wiki 对于笛卡尔树的解释。
不难发现,通过构建原序列的笛卡尔树,即可把原本的 问题转化成了树上 问题,继续转换,就变成了 问题。可以预处理 ,每次查询 ,共 的时间内解决。
问题定义为相邻两个值的差为 。即 。
引用一段文字(摘自 OI_wiki,进行了格式优化):
由于 算法的瓶颈在于块内 问题,我们重点去讨论块内 问题的优化。
由于相邻两个数字的差值为 ,所以在固定左端点数字时 长度不超过 的右侧序列种类数为 ,而这个式子显然不超过 。
这启示我们可以预处理所有不超过 种情况的最小值-第一个元素的值。
在预处理的时候我们需要去预处理同一块内相邻两个数字之间的差,并且使用二进制将其表示出来。
在询问的时候我们找到询问区间对应的二进制表示,查表得出答案。
这样子 预处理的时间复杂度就被优化到了 。
优化 :基于状压的线性 问题
对于同块的情况,考虑上述中用单调栈求解过程。令查询 最大值。 同块。
把 的元素放入单调栈,区间 的答案即为下标大于 的第一个在栈中的元素(因为维护单调性)。
考虑这样想:将 的元素加入单调栈时,可以用一个长度为块长 的 串表示 所在块的每个元素是否在栈内。显然当 时容易状压成一个整数。
那么查询时用位运算截取 的部分转换成求状压后整数中末尾连续的 的数量。容易用 做到。
那么要使 ,不妨就取 ,预处理复杂度就是 ,每次查询 ,总时间复杂度为 。
总结
| 算法名称 | 时间复杂度 | 空间复杂度 | 综合分析 |
|---|---|---|---|
| step1.ST 表 | 朴素简单,适合新手入门。 | ||
| step2.离线单调栈 | 空间复杂度较低,时间复杂度朴素。 | ||
| step3.线段树/树状数组 | 算法简单朴素,优点不突出。但好想且好写。熟记模板即可。 | ||
| step4.分块 | 常数较大,复杂度分析复杂,但实现简单 | ||
| 同上 | 优化。 | ||
| 同上 | 基于状压的优化。 |
习题
点个赞吧!
相关推荐
评论
共 13 条评论,欢迎与作者交流。
正在加载评论...