详解RMQ-ST算法 ST模板
RMQ问题是求解区间最值的问题。
这里分析的是ST算法,它可以对所有要处理的数据做到O(nlogn)的预处理,对每个区间查询做到O(1)查询
ST算法本质是一个DP的过程
这里通过举一个求最大值实例来理解ST算法:
我们有这样一串数字
数值:35 13 65 99 88 75 64 51 42 55 66 83 12 44 65 12
位置:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
首先我们定义一个dp表达式:st[i][j]表示从i位置开始的2^j个数中的最大值;
具体解释:st[1][0]就是从第一个数字开始的一个数里的最大值,也就是第一个数本身,即st[1][0]=35;
st[2][2]就是从第二个数字开始的四个数里的最大值,也就是13,65,99,88里面的最大值,即st[2][2]=99;
以此类推
然后我们来看怎么用dp的思想来解决这个问题
回到刚刚的实例,我们由我们所定义的st式可以得知st[5][3]是[88,75,64,51,42,55,66,83]中的最大值。
现在我们来试着用dp的思想,也就是将整体化为部分求解的思想。
要求前面st[5][3]所代表区间的最大值,也就是求[88,75,64,51]和[42,55,66,83]两个区间的最大值中的较大值,即st[5][2]和st[9][2]
而这样的划分是一个二分划分,根据这种思想我们可以类推出,求i至其后2^j个数的最大值,即把2^j分成前后两个2^(j-1),分别取最大值,再通过比较获得此状态最大值。
到此我们可以得出我们的dp表达式:st[i][j] = max( st[i][j-1],st[i+2^(j-1)][j-1] )
通过这种方法我们可以求出一段段区间的最大值
求ST表代码
void cal_st( int n, int a[] ) { //n为区间元素个数,a数组存的是区间里的元素
for( int i = 1; i <= n; i ++ ) {
st[i][0] = a[i];
}
for( int j = 1; j <= log2(n); j ++ ) {
for( int i = 1; i <= n-(1<<j)+1; i ++ ) {
st[i][j] = max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
}
接下来让我们回到RMQ问题:
我们可以知道,任意的i至j之间的j-i+1个连续的值,一定可以分为两个2^n个数的两个区间
比如求3到11之间的最大值;
因为3到11之间有9个元素,最大可以成8个元素大小的区间;
所以我们可以将其分为[3,10]和[4,11]两个区间;(分成的两个区间一个是从开头取八个,一个是从最后往前取八个)
然后通过求这两个区间最大值中的较大值得到3到11之间的最大值
抽象成一个广义数学问题:
求[i,j]区间最大值;
num = j-i+1; p = 2^((int)(log2(num)));
rmq(i,j) = max( st[i][p], st[i-2^p+1][p] )
num:[i,j]区间元素个数,p:[i,j]区间可以连续分成的最大2^n区间的大小
rmq(i,j):查询[i,j]区间最大值
rmq查询代码:
int rmq( int le, int ri ) { //le为查询区间开始位置,ri为查询区间结束位置
int p = log2(ri-le+1);
return max(st[le][p],st[ri-(1<<p)+1][p]);
}
参考博客:https://blog.csdn.net/z287438743z/article/details/8132806
例题:洛谷P3865
题目背景
这是一道ST表经典题——静态区间最大值
请注意最大数据时限只有0.8s,数据强度不低,请务必保证你的每次查询复杂度为 O(1)
题目描述
给定一个长度为 N 的数列,和 M 次询问,求出每一次询问的区间内数字的最大值。
输入输出格式
输入格式:
第一行包含两个整数 N, M ,分别表示数列的长度和询问的个数。
第二行包含 N个整数(记为 ai ),依次表示数列的第 i 项。
接下来 M 行,每行包含两个整数 li,ri ,表示查询的区间为[li,ri]
输出格式:
输出包含 M 行,每行一个整数,依次表示每一次询问的结果。
输入输出样例
8 8
9 3 1 7 5 6 0 8
1 6
1 5
2 7
2 6
1 8
4 8
3 7
1 8
9
9
7
7
9
8
7
9
说明
对于30%的数据,满足:1≤N,M≤10
对于70%的数据,满足: 1≤N,M≤105
对于100%的数据,满足: 1≤N≤105,1≤M≤106,ai∈[0,10^9],1≤li≤ri≤N
分析:一个st表的模板题
st[i][j]表示以第i个数为首的一共2^j个数的最大值
ai表示原数列
可以得到:
if( j == 1 ) {
st[i][j] = a[i]
} else {
st[i][j] = max( st[i][j-1], st[i+(1<<(j-1))][j-1] );
}
AC代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <string>
#include <bitset>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
#define ls (r<<1)
#define rs (r<<1|1)
#define debug(a) cout << #a << " " << a << endl
using namespace std;
typedef long long ll;
const ll maxn = 1e5+10;
const ll mod = 998244353;
const double pi = acos(-1.0);
const double eps = 1e-8;
ll st[maxn][20]; //ll表示long long,个人习惯整数定义成long long
void cal_st( ll n, ll a[] ) {
for( ll i = 1; i <= n; i ++ ) {
st[i][0] = a[i];
}
for( ll j = 1; j <= log2(n); j ++ ) {
for( ll i = 1; i <= n-(1<<j)+1; i ++ ) {
st[i][j] = max(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
}
} ll rmq( ll le, ll ri ) {
ll p = log2(ri-le+1);
return max(st[le][p],st[ri-(1<<p)+1][p]);
}
int main() {
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll n, t, a[maxn];
scanf("%lld%lld",&n,&t);
for( ll i = 1; i <= n; i ++ ) {
scanf("%lld",&a[i]);
}
cal_st(n,a);
while( t -- ) {
ll le, ri;
scanf("%lld%lld",&le,&ri);
printf("%lld\n",rmq(le,ri));
}
return 0;
}
详解RMQ-ST算法 ST模板的更多相关文章
- 【机器学习详解】SMO算法剖析(转载)
[机器学习详解]SMO算法剖析 转载请注明出处:http://blog.csdn.net/luoshixian099/article/details/51227754 CSDN−勿在浮沙筑高台 本文力 ...
- 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)
参考网址:图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS) - 51CTO.COM 深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath ...
- RMQ 问题 ST 算法(模板)
解决区间查询最大值最小值的问题 用 $O(N * logN)$ 的复杂度预处理 查询的时候只要 $O(1)$ 的时间 这个算法是 real 小清新了 有一个长度为 N 的数组进行 M 次查询 可 ...
- 机器学习--详解人脸对齐算法SDM-LBF
引自:http://blog.csdn.net/taily_duan/article/details/54584040 人脸对齐之SDM(Supervised Descent Method) 人脸对齐 ...
- (新手向)N皇后问题详解(DFS算法)
非常经典的一道题: N皇后问题: 国际象棋中皇后的势力范围覆盖其所在的行.列以及两条对角线,现在考察如下问题:如何在n x n的棋盘上放置n个皇后,使得她们彼此互不攻击 . 免去麻烦我们这里假定n不是 ...
- 遗传学详解及Matlab算法实现
遗传学算法概述 从之前转载的博客<非常好的理解遗传算法的例子>中可以知道,遗传学算法主要有6个步骤: 1. 个体编码 2. 初始群体 3. 适应度计算 4. 选择运算 5. 交叉运算 6. ...
- TCP-IP详解:Nagle算法
在使用一些协议通讯的时候,比如Telnet,会有一个字节字节的发送的情景,每次发送一个字节的有用数据,就会产生41个字节长的分组,20个字节的IP Header 和 20个字节的TCP Header, ...
- leetcode-全排列详解(回溯算法)
全排列 给定一个没有重复数字的序列,返回其所有可能的全排列. 示例: 输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2 ...
- 详解使用EM算法的半监督学习方法应用于朴素贝叶斯文本分类
1.前言 对大量需要分类的文本数据进行标记是一项繁琐.耗时的任务,而真实世界中,如互联网上存在大量的未标注的数据,获取这些是容易和廉价的.在下面的内容中,我们介绍使用半监督学习和EM算法,充分结合大量 ...
- 机器学习——详解经典聚类算法Kmeans
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第12篇文章,我们一起来看下Kmeans聚类算法. 在上一篇文章当中我们讨论了KNN算法,KNN算法非常形象,通过距离公 ...
随机推荐
- poj2909 欧拉素数筛选
刚刚学了一种新的素数筛选法,效率比原先的要高一些,据说当n趋近于无穷大时这个的时间复杂度趋近O(n).本人水平有限,无法证明. 这是道水题,贴代码出来重点是欧拉筛选法.我把原来普通的筛选法贴出来. / ...
- poj 1455 Crazy tea party
这道题第一眼看去很难,其实不然,短短几行代码就搞定了. 说一下大概思路,如果是排成一排的n个人,如 1 2 3 4 5 6 7 8 我们要变成 8 7 6 5 4 3 2 1 需要交换 28次,找规律 ...
- JS 自执行函数
由于自己js基础知识薄弱,很多js的知识还没有掌握,所以接下来会经常写一些关于js基础知识的博客,也算给自己提个醒吧. js自执行函数,听到这个名字,首先会联想到函数.接下来,我来定义一个函数: fu ...
- 自定义仿 IPhone 开关控件
极力推荐文章:欢迎收藏 Android 干货分享 阅读五分钟,每日十点,和您一起终身学习,这里是程序员Android 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以 ...
- 如何用Hexo+Github创建自己的技术博客
注册一个github GitHub官网.按照一般的网站注册登录执行就好了,不详细说. 安装git 安装很简单,一直下一步 git安装教程 很多教程里都说要配置环境变量,我本人安装过5次左右的git,一 ...
- resolv.conf文件配置相关的案例
引言 操作系统中/etc/resolv.conf配置文件中的内容一般为空,如果该文件配置不正确,将导致ssh.route.netstat命令响应慢的问题. 在/etc/resolv.conf添加错误地 ...
- 如何获取app中的toast
前言 Toast是什么呢?在这个手机飞速发展的时代,app的种类也越来越多,那们在日常生活使用中,经常会发现,当你在某个app的输入框输入非法字符或者非法执行某个流程时,经常看到系统会给你弹出一个黑色 ...
- 通过Blazor使用C#开发SPA单页面应用程序(1)
2019年9月23——25日 .NET Core 3.0即将在.NET Conf上发布! .NET Core的发布及成熟重燃了.net程序员的热情和希望,一些.net大咖也在积极的为推动.NET Co ...
- JVM调优之经验
在生产系统中,高吞吐和低延迟一直都是JVM调优的最终目标,但这两者恰恰又是相悖的,鱼和熊掌不可兼得,所以在调优之前要清楚舍谁而取谁.一般计算任务和组件服务会偏向高吞吐,而web展示则偏向低延迟才会带来 ...
- Python依赖包整体迁移方法
1.新建site-packages目录,进入到site-packages目录下: 2.在site-packages目录下执行pip freeze >requirements.txt: 3.查看r ...