模板 RMQ问题ST表实现/单调队列
RMQ (Range Minimum/Maximum Query)问题是指:
对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,RMQ问题是指求区间最值的问题。
for循环遍历一边,然后输出,那么你很容易想到会被T飞掉;
1、先写一种比较高效的ST算法解决这个问题。
线段树预处理O(nlogn),查询O(logn),支持在线修改
ST表预处理O(nlogn),查询O(1),但不支持在线修改
其实ST表是一种动态规划的思想;每次运用倍增思想求解每一个区间;
一个序列的子区间有o(n^2)个,根据倍增思想,我们首先在这个规模中选择一些2的正实数次幂作为代表值;
定义:f(i,j)表示[i,i+2j−1] 这段长度为2j 的区间中的最大值。也就是从i开始2j个数的最大值;
边界:f(i,0)=ai。即f[i,i]区间的最大值就是ai。
递推时我们把子区间的长度成倍增长,$f(i,j)=max{f(i,j−1),f(i+2j−1,j−1)}$;
状态转移:将[i,j]平均分成两段,一段为[i,i+2j−1],另一段为[i+2j−1,i+2j−1]。
即两段子区间的长度均为2j−1。f(i,j)的最大值即这两段的最大值中的最大值。
查询任意区间[l,r]时,我们先计算出一个k,满足2k<r-l+1<=2k+1;也就是使2k小于区间的前提下的最大的k,
那么“从l开始2k个数”和以r结尾的2k个数,这两段一定覆盖了整个区间[l,r],
这两段的最大值分别是f[l,k],f[r-2k+1,k],两个中较大的就是整个区间[l,r]的最值,即使重叠也没有关系;
网上也有这样解释的;https://blog.csdn.net/Hanks_o/article/details/77547380
一个查询最小值的问题:质量检测
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x)
{
x=;T f=,ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
x*=f;
}
int n,m,f[][],x,y;
inline void Rmq(int x)
{
for(int i=; i<=; i++)
{
for(int j=; j<=x; j++)
{
if(j+(<<i)-<=x)//使j+(1<<i)-1不超过区间最右端;
f[j][i]=min(f[j][i-],f[j+(<<(i-))][i-]);//动态规划;
}
}
}
inline int query(int x,int y)
{
int k=log2(y-x+);
return min(f[x][k],f[y-(<<k)+][k]);
}
int main()
{
read(n);read(m);
for(int i=;i<=n;i++)
read(f[i][]);
Rmq(n);
for(int i=;i<=n-m+;i++)
printf("%d\n",query(i,i+m-));
return ;
}
毕竟最大最小改一下min,max就可以了;
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x)
{
x=;T f=,ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
x*=f;
}
int n,m,f[][],x,y,a[];
inline void Rmq(int x)
{
for(int i=; i<=; i++)
{
for(int j=; j<=x; j++)
{
if(j+(<<i)-<=x)
f[j][i]=min(f[j][i-],f[j+(<<(i-))][i-]);
}
}
}
inline int query(int x,int y)
{
int k=log2(y-x+);
return min(f[x][k],f[y-(<<k)+][k]);
}
int main()
{
read(n);read(m);
for(int i=;i<=n;i++)
read(f[i][]);
Rmq(n);
for(int i=;i<=m;i++)
{
read(x);read(y);
a[i]=query(x,y);
}
for(int i=;i<=m;i++)
printf("%d ",a[i]);
return ;
}
但有点变态的是,这个过不了洛谷的1440,所以我们再来讲一下单调队列的实现方式;
例题:滑动窗口
这个单调队列讲解好强:https://www.luogu.org/blog/hankeke/solution-p1886
本题样例:
8 3
1 3 -1 -3 5 3 6 7
下文中我们用q来表示单调队列,p来表示其所对应的在原列表里的序号。
由于此时队中没有一个元素,我们直接令1进队。此时,q={1},p={1}。
现在3面临着抉择。下面基于这样一个思想:假如把3放进去,如果后面2个数都比它大,那么3在其有生之年就有可能成为最小的。此时,q={1,3},p={1,2}
下面出现了-1。队尾元素3比-1大,那么意味着只要-1进队,那么3在其有生之年必定成为不了最小值,原因很明显:因为当下面3被框起来,那么-1也一定被框起来,所以3永远不能当最小值。所以,3从队尾出队。同理,1从队尾出队。最后-1进队,此时q={-1},p={3}
出现-3,同上面分析,-1>-3,-1从队尾出队,-3从队尾进队。q={-3},p={4}。
出现5,因为5>-3,同第二条分析,5在有生之年还是有希望的,所以5进队。此时,q={-3,5},p={4,5}
出现3。3先与队尾的5比较,3<5,按照第3条的分析,5从队尾出队。3再与-3比较,同第二条分析,3进队。此时,q={-3,3},p={4,6}
出现6。6与3比较,因为3<6,所以3不必出队。由于3以前元素都<3,所以不必再比较,6进队。因为-3此时已经在滑动窗口之外,所以-3从队首出队。此时,q={3,6},p={6,7}
出现7。队尾元素6小于7,7进队。此时,q={3,6,7},p={6,7,8}。
那么,我们对单调队列的基本操作已经分析完毕。因为单调队列中元素大小单调递*(增/减/自定义比较),因此,队首元素必定是最值。按题意输出即可。
#include<bits/stdc++.h>
using namespace std;
template<typename T>inline void read(T &x)
{
x=;T f=,ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-; ch=getchar();}
while(isdigit(ch)) {x=x*+ch-''; ch=getchar();}
x*=f;
}
int head,tail,q[],p[],k,n,a[];
inline void maxn()
{
head=;tail=;
for(int i=;i<=n;i++)
{
while(head<=tail&&q[tail]<=a[i])
--tail;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k)
head++;
if(i>=k) printf("%d ",q[head]);
}
putchar('\n');
}
inline void minn()
{
head=;tail=;
for(int i=;i<=n;i++)
{
while(head<=tail&&q[tail]>=a[i])
--tail;
q[++tail]=a[i];
p[tail]=i;
while(p[head]<=i-k)
head++;
if(i>=k) printf("%d ",q[head]);
}
putchar('\n');
}
int main()
{
read(n);read(k);
for(int i=;i<=n;i++)
read(a[i]);
minn();
maxn();
return ;
}
模板 RMQ问题ST表实现/单调队列的更多相关文章
- 使用类模板的C++线性表实现(数组方式)
main.h #ifndef _MAIN_H_ #define _MAIN_H_ #include <iostream> #include <exception> #inclu ...
- POJ 2823 Sliding Window (模板题)【单调队列】
<题目链接> <转载于>>> > 题目大意: 给你一段序列和一个长为k的窗口,这个窗口从最左边逐渐向右滑,直到滑到最右边,问你,该窗口在滑动的过程中,最大值和 ...
- 洛谷 P1886 滑动窗口(单调队列)
嗯... 题目链接:https://www.luogu.org/problem/P1886 首先这道题很典型,是标准的单调队列的模板题(也有人说单调队列只能解决这一个问题).这道题可以手写一个队列,也 ...
- POJ1821 单调队列//ST表 优化dp
http://poj.org/problem?id=1821 当我们在考虑内层循环j以及决策k的时候,我们可以把外层变量i看作定值,以此来优化dp状态转移方程. 题意 有n个工人准备铺m个连续的墙,每 ...
- 【模板】deque实现单调队列
双端队列deque容器: 关于deque最常用的有这几个函数: 都是成员函数 双端队列模板题:[洛谷]P2952 [USACO09OPEN]牛线Cow Line #include<iostrea ...
- HDU - 5289 Assignment (RMQ+二分)(单调队列)
题目链接: Assignment 题意: 给出一个数列,问其中存在多少连续子序列,使得子序列的最大值-最小值<k. 题解: RMQ先处理出每个区间的最大值和最小值(复杂度为:n×logn),相 ...
- HDU3183 A Magic Lamp —— 贪心(单调队列优化)/ RMQ / 线段树
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3183 题解: 方法一:贪心. 在草稿纸上试多几次可以知道,删除数字中从左到右最后一位递增(可以等于)的 ...
- [Bzoj4540][Hnoi2016] 序列(莫队 + ST表 + 单调队列)
4540: [Hnoi2016]序列 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 1567 Solved: 718[Submit][Status] ...
- cf1208 E Let Them Slide(差分+RMQ\单调队列)
题意 如题目的图所示,每行都可以左右移动,但是数字不允许断开,且不许越界(宽度为w). 单独求每一列的最大的和为多少. 思路 对于每一列来说,在每一行上都有一个可以取到的区间, 所以,对于一列来说,答 ...
随机推荐
- 单片机stm32小白入门级学习路线“图”
学习stm32的是真的越来越多了,当然我也是其中语言,所以对于stm32的学习路线非常的感兴趣,所以我也分享一下 虽然是盗图吧 不过也算是分享 ,下边有觉得不错的视频资料 也奉上 (stm ...
- js基础 -函数
函数 定义 var a =function (){...}; 匿名函数方式定义function a(){} 直接定义 函数的参数arguments 可以接收任意个参数,是个像数组的内容,可for in ...
- HTML+CSS+JavaScript-案例
CSS-flex弹性布局案例1: HTML代码: <!DOCTYPE html> <html lang="en"> <head> <met ...
- uCOS-II
/****************************************************/ **关于移植,ucos官网上给的有template,主要思想是实现任务切换的两个函数(任务 ...
- wordCount剖析Spark模型
- Tea for Mac(mac笔记软件)中文版
为大家分享一款好用且免费的mac笔记软件,Tea for Mac提供了实时渲染的Markdown,功能全面,支持各种快捷键,使用tea mac版时,在段首打@即可快速插入图片.标题.列表等元素,非常便 ...
- java mysql连接时出现的问题
当出现Caused by: java.sql.SQLException: Unknown system variable ‘tx_isolation’ 一般是mysql-connector-java的 ...
- C++实验三
part2 graph.h #ifndef GRAPH_H#define GRAPH_H// 类Graph的声明 class Graph { public: Graph(char ch, int n) ...
- Matlab文件和数据的导入与导出
ref: https://blog.csdn.net/zengzeyu/article/details/72530596 Matlab文件和数据的导入与导出 2017年05月19日 15:18:35 ...
- “QObject调用moveToThread()后 该如何释放”及QThread 的启动关闭
1 QThread *thread = new QThread( ); 2 Task *task = new Task(); 3 task->moveToThread(thread); 4 co ...