众所周知单调队列优化一般都用于求区间最大/小值
我们做DP题的时候,有时会遇到像这样子的方程
[f(i)=a(i)-min/max(w(k)) , i-Lleq kleq i
]
Warning这儿的w必须和i一点关系都没有,a必须和k一点关系都没有
为了方便描述,以下叙述就当做(f(i)=a(i)-min(w(k)))
可以发现这个转移方程复杂度是(O(nL))的
也可以用一些数据结构变成(O(nlog n))
不过我们有更好的方法
就是拿单调队列维护(min(w(k)), i-Lleq kleq 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;
}