【学习笔记】DP 优化

目录

  1. ds 优化
  2. 单调队列优化
  3. 斜率优化
  4. 决策单调性优化

1. ds 优化

考虑枚举右端点 \(i\),求出 \(\sum\limits_{j=1}^if(j,i)\)

假设我们维护了一个序列,\(j\) 位置存的是 \([j,i]\) 的答案。

先预处理出 \(lst_i\),表示 \(i\) 往左数第一个等于 \(a_i\) 的数的下标。

如果右端点从 \(i-1\) 变成 \(i\),发现对于所有 \(j~(lst_i+1<j\le i)\),它们的答案都要加 \(1\)。而对于 \(j~(j\le lst_i)\),由于 \(a_i\) 已经在 \(lst_i\) 上出现过,答案不变。

本题还要求出平方和,用线段树维护即可。

枚举建几个基站,建 \(t\) 个基站的答案可以从 \(t-1\) 转移过来。

\(f_i\) 表示在第 \(i\) 给村庄建一个基站,已经考虑了 \([1,i-1]\) 内的村庄的补偿,最少需要多少钱。

如果我们要求 \(f_i\),维护一个线段树,\(j\) 位置表示上一个基站在 \(j\) 村庄时(即从 \(j\) 转移),\(f_i\) 的值。

预处理 \(l_i,r_i\) 表示坐标在 \([d_i-s_i,d_i+s_i]\) 内的最左边和最右边的村庄。

\(f_i\) 是很好求的,只需要求个最小值。考虑如何更新这个序列。

如果有一个村庄 \(k\)\(r_k=i\),那么当我们求 \(f_{i^\prime}~(i^\prime>i)\) 时,如果从 \(j~(j<l_k)\) 转移,那么覆盖不到 \(k\),需要支付 \(w_k\)

用 vector 或前向星,在 \(i\) 位置存所有 \(r_k=i\)\(k\)。每次求出 \(f_i\) 时,枚举这里面的 \(k\),在 \([1,l_k-1]\) 上加上 \(w_k\)

求出所有 \(f\) 后,重建线段树,把 \(f_i\) 放到 \(i\) 位置上,用于求建 \(t+1\) 个基站的答案。

练习题

2. 单调队列

\(f_i=\min\limits_{d_i\le j\le i}\{a_i\}\),其中 \(d_i\) 单调不减。

显然可以 st 表 \(O(n\log n)\),但有个 \(d_i\) 单调不减的性质没用上。

考虑维护一个队列,求 \(f_i\) 时,里面从小到大存了所有在 \([1,i-1]\) 内的下标。

但这里面有很多元素是没有用的。

\(\forall j,k\in[1,i-1],j<k\)

  1. \(a_j\ge a_k\),考虑任意一个能被 \(j\) 更新到的 \(i~(i>k)\)(即 \(d_i\le j\)),必有 \(d_i\le j<k\)\(k\) 也能更新 \(i\)。而 \(a_j\ge a_k\)\(j\) 并不优于 \(k\),没必要存在于队列中。

  2. \(j<d_i\)\(j\) 无法更新 \(i\),而随着 \(i\) 增大 \(d_i\) 不会减小,即 \(d_i\) 再也没有用了。

综上,这个队列应时刻满足:

  1. 下标递增
  2. \(a_j\) 递增
  3. 队首 \(\ge d_i\)

\(f_i\) 时只需取出队首,因为它已经是最小的了。然后从队尾删除直到队尾的值 \(<a_i\),插入 \(i\)

容易发现每个元素最多进、出各一次,所以时间复杂度 \(O(n)\)

练习题

3. 斜率优化

\(c\) 做前缀和。设 \(f_i\) 表示以第 \(i\) 个玩具作为一个容器的最后一个玩具,前 \(i\) 个玩具的最小费用。

\(f_i=\min\limits_{1\le j<i}\{f_j+(j-i-1+c_i-c_j-L)^2\}\)

若令 \(L\)\(1\),再令 \(c_k\) 加上 \(k\),方程变成 \(f_i=\min\limits_{1\le j<i}\{f_j+(c_i-c_j-L)^2\}\)

但它并不能用单调队列,因为展开后同时含有 \(c_i\times c_j\)\(c_j^2\)

考虑计算 \(f_i\) 时,选择 \(j,k~(j<k)\) 作为转移,如果 \(j\) 优于 \(k\)

\(f_j+(c_i-c_j-L)^2<f_k+(c_i-c_k-L)^2\)

\(f_j+c_j^2-2c_ic_j-2c_jL<f_k+c_k^2-2c_ic_k-2c_kL\)

\((f_j+c_j^2-2c_jL)-(f_k+c_k^2-2c_kL)<2c_i(c_j-c_k)\)

\(\dfrac{(f_j+c_j^2-2c_jL)-(f_k+c_k^2-2c_kL)}{c_j-c_k}>2c_i\)

对于第 \(t\) 个决策,在平面直角坐标系内找一点 \(P_t(c_t,f_t+c_t^2-2c_tL)\)。用 \(k_{j,k}\) 表示 \(P_j\)\(P_k\) 连成线段的斜率,令\(K_i=2c_i\)。那么对于 \(i\)\(j\) 优于 \(k\) 当且仅当 \(k_{j,k}>K_i\)

仿照单调队列,我们维护一个队列,求 \(f_i\) 时,里面从小到大存了所有在 \([1,i-1]\) 内的下标。

这里面有很多点是没有用的。

  1. \(\forall x_1,x_2,x_1<x_2,k_{x_1,x_2}\le K_i\),此时 \(x1\) 不优于 \(x_2\)。因为 \(c_i\) 单调递增,\(K_i\) 也单调递增,对于 \(i^\prime~(i^\prime>i)\)\(x1\) 仍不优于 \(x_2\)。所以 \(x_1\) 是没用的。
  2. \(\forall x_1,x_2,x_3,x_1<x_2<x_3,k_{x_1,x_2}\ge k_{x_2,x_3}\),对于任意的 \(i\)\(k_{x_2,x_3}>K_i\),则 \(x_2\) 优于 \(x_3\)。而此时必有 \(k_{x_1,x_2}>K_i\),即 \(x_1\) 优于 \(x_2\)。所以 \(x_2\) 是没用的。

所以我们要维护一个斜率递增的下凸壳,每次看队首和队首的后一个元素的斜率,若 \(\le K_i\) 弹出队首。不断重复直到斜率大于 \(K_i\),然后从队首转移到 \(i\)。不断弹出队尾直到队尾的前一个元素和队尾的斜率小于队尾和 \(i\) 的斜率。然后在队尾插入 \(i\)

有些题目会出现不存在斜率的情况(即两点横坐标相同),此时要特判并返回 \(\infty\)\(-\infty\)(视具体情况而定)。


刚刚我们解决了 \(c_j\) (即决策点横坐标)单调递增,\(K_i\) 也递增的情况。

但有些题目决策点横坐标单调递增,但 \(K_i\) 不一定。此时刚才的第一条性质不一定满足,我们就不能从队首删点。此时我们就要在队列里二分,找到第一个点,满足它和下一个元素的斜率大于(或小于,视题目而定)\(K_i\),并把它作为决策点。

有些题目甚至决策点横坐标和 \(K_i\) 都不单调,此时就要用平衡树或 CDQ 分治。

练习题

posted @ 2021-05-25 16:41  EverlastingEternity  阅读(82)  评论(0编辑  收藏  举报