可查询双端队列学习笔记

可查询双端队列

操作:头插头删,尾插尾删,查最大值。

做法:从中点维护向左、向右两个单调栈(不强制加入),如果一个单调栈被删空了就重构。

复杂度证明:一次重构的复杂度是两个单调栈的长度差,而每次操作最多使长度差 +1,于是总复杂度是 \(O(n)\) 的。

P1295 [TJOI2011] 书架

题意:给一个序列 \(h\),要分成若干段使得每一段长度不大于 \(m\),最小化所有段的最大值之和。

思路:首先可以很简单的想到一个 \(O(n^2)\) 的 DP:设 \(f_i\) 表示 \(i\) 为当前段末尾的最小和,则 \(f_i=\min\limits_{s_i-s_j<=m}(f_j+\max\limits_{k=j+1}^ih_k)\)

因为有最大值,我们考虑找找单调性。首先,如果 \(h_j\leqslant h_{j+1}\),那么 \(\max\limits_{k=j}^i h_k=\max\limits_{k=j+1}^i h_k\),而 \(f\) 显然是单调递增,那这个时候从 \(j\)转移一定比从 \(j+1\) 转移更优,所以我们很容易用一个单调队列(记为 \(q\))来记录可能对答案做贡献的位置。然后我们考虑如何计算贡献。因为队列里的值是递减的,即 \(h_{q_l}>\cdots h_{q_r}\),那么每个的贡献就是 \(f_{q_l}+a_{q_{l+1}}\),这时我们还需维护 \(f\)。为了保证复杂度,我们可以用两个单调下降的栈来维护 \(f\),每当左或右端点到了中点就重构,这样复杂度就是 \(O(n)\) 的了。

posted @ 2024-02-13 11:41  Xttttr  阅读(3)  评论(0编辑  收藏  举报