让菜鸡讲一讲单调队列优化

众所周知单调队列优化一般都用于求区间最大/小值

我们做DP题的时候,有时会遇到像这样子的方程

\[f(i)=a(i)-min/max(w(k)) , i-L\leq k\leq i \]

Warning这儿的w必须和i一点关系都没有,a必须和k一点关系都没有

为了方便描述,以下叙述就当做\(f(i)=a(i)-min(w(k))\)

可以发现这个转移方程复杂度是\(O(nL)\)

也可以用一些数据结构变成\(O(n\log n)\)

不过我们有更好的方法

就是拿单调队列维护\(min(w(k)), i-L\leq k\leq i\)

这个队列里面是存放的k,而不是w(k)

在每一次DPf(i)的时候,对于这个新的i,我们

1.先删掉前面超出范围(\(<i-L\))的队头。

单调队列中的数都在要范围之内

2.将这个数和队尾比较,若队尾不比它优,就删掉队尾,直到队列为空或队尾比它优。最后将它加进队尾。

更新min

3.用队头转移。

队头最优(否则会在第2步被删掉)

比如说现在我们已经有一个单调队列

now:i=9,i-L=2,w[i]=5

q      he  1  3  5  6  8  ta
q对应的w    2  4  7  9  13

现在i=9,i-L=2,那么队头的1就已经过期了,于是

q      he  3  5  6  8  ta
q对应的w    4  7  9  13

而现在我们有一个9,对应的w是5,

现在我们来比较队尾

首先队尾有一个8,对应的w是13,

那么这个13比5要大,而位置8也要比9靠前,所以它永远不能变成最小值。

那么我们把它弹掉

q      he  3  5  6  ta
q对应的w    4  7  9

同理,把6和5也弹掉

q      he  3  ta
q对应的w    4

只剩下不能被继续弹掉的3了。

现在我们把9插进去

q      he  3  9  ta
q对应的w    4  5

转移的时候用w(q[he]),也就是w(3)转移就好了。

可以发现这样子的话,时间复杂度就变成了\(O(n)\)

给你一个模板题

以及代码

#include<bits/stdc++.h>
using namespace std;
inline int gotcha()
{
    register int a=0,b=1,c=getchar();
    while(!isdigit(c))b^=c=='-',c=getchar();
    while(isdigit(c))a=(a<<3)+(a<<1)+c-48,c=getchar();
    return b?a:-a;
}
const int _ = 500002;
int n,lim,s[_],q[_],he,ta;
int main()
{
    register int i,mx=-2e9;
    n=gotcha(),lim=gotcha();
    for(i=1,s[0]=0;i<=n;i++)s[i]+=s[i-1]+gotcha();
    for(i=1,he=ta=0;i<=n;i++)
    {
        while(he<=ta && q[he]+lim<i)he++;
        while(he<=ta && s[q[ta]]>=s[i])ta--;
        q[++ta]=i,mx=max(mx,s[i]-s[q[he]]);
    }
    printf("%d",mx);
    return 0;
}
posted @ 2018-02-05 09:50  iot;  阅读(213)  评论(1编辑  收藏  举报
知识共享许可协议
年轻人,你需要更多的知识