单调队列优化学习笔记
0.什么是单调队列
废话自己去学
1.板子:P1886 滑动窗口 /【模板】单调队列
2.用处:动态维护一段连续区间的最值,该区间的左右端点均单调不减。
1.为什么要用单调队列
看一道例题:
P1714 切蛋糕
今天是小 Z 的生日,同学们为他带来了一块蛋糕。这块蛋糕是一个长方体,被用不同色彩分成了 \(n\) 个相同的小块,每小块都有对应的幸运值。
小 Z 作为寿星,自然希望吃到的蛋糕的幸运值总和最大,但小 Z 最多又只能吃 \(m(m\le n)\) 小块的蛋糕。
请你帮他从这 \(n\) 小块中找出连续的 \(k(1 \le k\le m)\) 块蛋糕,使得其上的总幸运值最大。
形式化地,在数列 \(\{p_n\}\) 中,找出一个子段 \([l,r](r-l+1\le m)\),最大化 \(\sum\limits_{i=l}^rp_i\)。
对于 \(100\%\) 的数据,有 \(1\le n\le5\times 10^5\),\(|p_i|≤500\)。
我们有一个很显然的暴力算法
设\(\small sum[i]\)表示到第\(\small i\)个蛋糕价值的前缀和,\(\small ans\)为答案
时间复杂度为\(\small O(n^2)\),显然不可接受
而运用单调队列优化,就可以将复杂度降至\(\small O(n)\),便可以轻松水过这题。
2.如何用单调队列
1.开胃小菜
对于\(\small \min(sum[j])\),我们发现它实质上是要维护一个区间的最小值,且左右端点均单调不减
因此可以用单调队列去动态维护\(\small \min(sum[j])\)
具体来说,对于双端队列\(\small q\),当前值\(\small i\)
- 不断弹出队首\(\small head\)直至\(\small head\)位于\(\small [\max(0,i-m),i)\)中
- 用\(\small sum[head]\)更新\(\small ans\)
- 不断弹出队尾\(\small tail\)直至\(\small sum[tail]<sum[i]\)
- 将\(\small sum[i]\)插入队尾
- 更新\(\small i\)
我们从这题一窥单调队列优化:
在这道题中,我们用单调队列维护了一个单调递增的区间,并以队首更新答案
Q:为什么是单调递增,它不是取最小值吗?
A:这是我学习单调队列时一直绕不开的思路误区。我们是用队首更新,因此要一直保持队首最小。若为单调递减则队首会一直保持最大,举几个例子就知道了
Q:1操作有什么用?
A:保证队列里的值的位置均位于所查找的区间中。
Q:为什么只需维护\(\small \min(sum[j])\),\(\small sum[i]\)不是也在一直变化吗?
A:我们在选定\(\small i\)的时候,只与\(\small i\)有关的量就变为常量,原式就会变为
对于一个常量,为什么要维护呢?
2.开胃大菜
通过上文的讲述,我们建立起了对单调队列优化一个初步的认识,现在我们推广一下
对于如下的动规方程
其中\(\small F[i]\)表示在第\(\small i\)状态下的取值,\(\small L(i),R(i)\)分别表示与\(\small i\)有关的一次函数,且具有相同单调性,\(\small val(i,j)\)表示与该函数中每一项都只与\(\small i,j\)的其中一项有关
在选定\(\small i\)时,\(\small i\)就变为常量,因此可以将方程化为
我们维护一个单调队列,保持队首\(\small F[j]+val(j)\)最小,同时\(\small j\)在\(\small [L(i),R(i)]\)范围内,每次用队首更新\(\small F[i]\),即可用\(\small O(n)\)的时间解决这类题目
3.一些细节
- 如果题目的\(\small R(i)-L(i)\)不是一个定值,更新队首时要注意取值范围
- 使用单调队列优化的要求\(\small L(i),R(i)\)均单调不减
重要的事情说三遍;\(\small val(i,j)\)中的项只与\(\small i\)或\(\small j\)有关。
3.试看看(不是)
P1725 琪露诺
P2034 选择数字(自认为此题比上题简单)
P2216 [HAOI2007]理想的正方形
P2627 [USACO11OPEN]Mowing the Lawn G
P3572 [POI2014]PTA-Little Bird
本文来自博客园,作者:cmd_pig,转载请注明原文链接:https://www.cnblogs.com/cmd-pig/articles/16548761.html

浙公网安备 33010602011771号