->知识点整理-单调队列<-
知识点讲解:
既然在模板库中发了单调队列的板子,那么就顺便把单调队列讲一讲吧。(我不会说是在NOIP前一天攒一攒RP的~)单调队列和单调栈的方法差不多,而且可以算是不会线段树的OIer的福音了。所以单调栈也不会的同学也来学一学吧,反正操作也是差不多的。
回归正题~单调队列的用途是维护一个区间的最值的(是不是很像线段树啊~咳咳…),一般用在求数据范围比较大的题目中(废话~不然暴力都过了)。其实简单的说,只有几句话,以维护区间最小值为例:当队列尾部的元素大于即将进入队列的元素时,就不断的让队尾元素出列,直到队尾元素小于即将进入队列的元素或者队列为空为止。然后假设我们要维护一个区间长为m的最小值,当我们队尾的元素在原数组中所对应的下标减去队首元素的下标大于等于m的时候,我们就需要把队首元素出列。
下面是讲解:首先既然是单调队列,那么队列中的元素就必须要一直保持一个单调性,不能因为入队列的元素而打乱顺序,这是通过定义上来解释第一个操作的;而通过队列中元素的作用的方法来解释就是:当进入队列的元素进入后,通过我们的操作,队列中的元素一定都是当前元素下标pos-m+1到pos中的元素,所以说当我们要维护最小值的时候,那么比当前进入队列的元素大的元素都不会对结果造成影响了,所以可以出队列了(这里需要好好消化一下,理解了这里就可以简单的写出单调队列了)。然后是第二个操作,也是比较好理解的,因为当队首元素的下标与队尾元素的下标差超过m的时候,就意味着这个元素已经不在即将处理的长度为m的区间的最小值范围内了,也就不会对结果造成影响了,也可以出队列了。
知识点实现:
首先,上面讲到的单调队列是需要在队首和队尾出列的,所以我们还需要用到双向队列deque,然后就是操作了 ,实际上只要理解了上面的讲解,并且在细节上加以处理,就可以了,所以直接放出代码吧:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2000005;
typedef long long ll;
typedef pair<ll,int>P;
#define fi first
#define se second
inline void read(int &x)
{
x=0;int f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
x*=f;
}
inline void read(ll &x)
{
x=0;int f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
x*=f;
}
int n,m;
ll a[maxn];
ll minn[maxn];
deque<P>Q;//双向队列,里面用pair(只是个人比较喜欢,也可以用结构体来存),分别记录数据和下标
void get_min()
{
int now=1;
Q.push_back(P(a[now],now));
minn[now]=a[now];//入队第一个元素,并且直接可以更新当前的最小值了(因为前面没有数了)
now++;
while(now<=n)//不断将数据入队
{
// printf("%d\n",now);
while(!Q.empty()&&Q.back().fi>a[now])//把队尾中比即将入队的元素小的元素都出队,注意判一下队列是否为空
{
Q.pop_back();
}
Q.push_back(P(a[now],now));
P top=Q.back();
P fr=Q.front();
if(top.se-fr.se>=m)//当队首元素和队尾元素的下标的差大于等于m的时候,把队首元素出队
{
Q.pop_front();
}
minn[now]=Q.front().fi;//记录当前的最小值为队首元素
now++;
}
}
int main()
{
read(n);
read(m);
for(int i=1;i<=n;i++)
{
read(a[i]);
}
get_min();
minn[0]=0;
for(int i=0;i<n;i++)
{
printf("%lld\n",minn[i] );
}
}