dp tricks
好多好杂,一个个放真的会炸掉。
决策单调性
四边形不等式优化
对 \(w_{i,j}\),若 \(\forall a \leq b \leq c \leq d,w_{a,d}+w_{b,c}\geq w_{a,c}+w_{b,d}\) ,则称 \(w\) 满足四边形不等式关系,该不等式为四边形不等式。
几何意义
可以由三角形不等式证出凸四边形 \(ABCD\) 中,\(AD+BC \geq AB+CD\),这就是四边形不等式的几何意义。
性质:满足 \(w_{i,j+1}+w_{i+1,j} \geq w_{i,j}+w_{i+1,j+1}\) 的 \(w\) 满足四边形不等式关系。
证明:
记上式为(1)式。用 \(i+1\) 改写(1)式,得到:\(w_{i+1,j+1}+w_{i+1,j+1}\geq w_{i+1,j}+w_{i+2,j+1}\),记为(2)式。两式相加,得到:\(w_{i,j+1}+w_{i+1,j} \geq w_{i,j}+w_{i+2,j+1}\);用 \(i+2\) 改写(1)式,同理可得 \(w_{i,j+1}+w_{i+3,j}\geq w_{i,j}+w_{i+3,j+1}\),以此类推,我们可以得到 \(w_{a,c+1}+w_{b,c}\geq w_{a,c}+w_{b,c+1}\),我们用同样的方式就可以推出对 \(a,b,c,d\) 不等式都是正确的,得证。
这个性质多用于证明 \(w\) 满足四边形不等式关系,但我们一般打个表出来,观察到符合性质就直接写优化了。这并不严谨,但场上很好用。
优化 DP
一般地,四边形不等式用来优化形如 \(f_{i}=\min/\max_{j=1}^{i-1}\{f_{j}+v_{j,i}\}\) 的 DP 转移。 下面我们以 \(\min\) 为例说明四边形不等式优化 DP 的过程。
我们引入决策的概念。\(i\) 的决策点 \(p_i\) 表示 \(f_j+v_{j,i}\) 的最值取到的那个点,即 \(\min\limits_{j=1}^{i-1}\{f_j+v_{j,i}\}=f_{p_i}+v_{p_i,i}\)。考虑如何利用决策点与四边形不等式的性质进行优化。
性质1:若 \(v\) 满足四边形不等式关系,则决策点单调。
证明:反证法。若决策点不单调,即存在 \(c,d\) 使 \(b=p_c > p_d=a\),此时 \(a < b \le c < d\),根据最优化条件,\(v_{a,d} \leq v_{b,d}\) 且 \(v_{a,c} > v_{b,c}\),故 \(v_{a,d}-v_{b,d}\le 0 < v_{a,c}-v_{b,c}\),与四边形不等式矛盾。\(\square\)
性质2:若有 \(x<j\),存在 \(i>j\) 使 \(j\) 比 \(x\) 更优,则对于 \(i' > i\),取 \(j\) 比 \(x\) 同样更优。观察到性质1这条性质的特例。一定要注意 \(x<j\) 这个条件。
证明:由题意得 \(f_{x}+v_{x,i} > f_j+v_{j,i}\),
由四边形不等式有 \(v_{x,i'}+v_{j,i}> v_{x,i}+v_{j,i'}\)
移项得 \(v_{x,i'}-v_{x,i}>v_{j,i'}-v_{j,i}\)
与第一个式子相加,得 \(f_x+v_{x,i'}>f_{j}+v_{j,i'}\),\(\square\)
通用解法
一般的,我们使用单调队列+二分查找的方式对决策进行维护
例:[NOI2009] 诗人小G
小 G 是一个出色的诗人,经常作诗自娱自乐。但是,他一直被一件事情所困扰,那就是诗的排版问题。
一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的。小 G 给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远。显然排版时,不应改变原有的句子顺序,并且小 G 不允许把一个句子分在两行或者更多的行内。在满足上面两个条件的情况下,小 G 对于排版中的每行定义了一个不协调度, 为这行的实际长度与行标准长度差值绝对值的 \(P\) 次方,而一个排版的不协调度为所有行不协调度的总和。
小 G 最近又作了几首诗,现在请你对这首诗进行排版,使得排版后的诗尽量协调(即不协调度尽量小),并把排版的结果告诉他。
诗句的数量 \(n\leq 10^5\)。
设 \(f_{i}\) 表示在第 \(i\) 个诗句换行,前 \(i\) 句诗的最小不协调度,记每句诗的长度为 \(a_i\),前 \(i\) 首诗的长度和为 \(s_i\),则有转移
想要优化就只能往决策单调性上想了。打个表发现 \(w(j,i)=|s_i-s_j+i-j-L-1|^P\) 满足四边形不等式,于是考虑证明。
\(\text{proof.}\)
wqs 二分
我是论文搬运工。
引例:对于数列 \(\{a_n\}\),把它分成若干段,记每段和为 \(s_i\),求 \(\sum (s_i^2+c)\) 的最小值
我会李超树!设 \(f_i\) 表示在 \(i\) 处分段,前 \(i\) 段的最小值,记 \(s_i=\sum_{j=1}^ia_i\),有转移
在李超树上维护 \(-2s_ix+s_i^2+f_i\) 就可以了!斜率优化也可以!
太没有挑战了,让我们加强一下!
Luogu P4983 省去推式子后的问题
对于数列 \(\{a_n\}\),把它分成 恰好 \(m\) 段,记每段的和为 \(s_i\),求 \(\sum (s_i+1)^2\) 的最小值。
李超树或者说斜率优化的做法直接做的极限是 \(O(nm)\),究其原因是因为状态规模无法从 \(O(nm)\) 上降下来。
一次 \(\text{dp}\) 之后得到的段数有可能恰好是 \(m\),否则得到的段数小于 \(m\),或者大于 \(m\)。
闲话:人固有一死,或重于泰山,或 Qingyu 等于泰山,终于等于鸿毛,或轻于鸿毛。
我们考虑用引例中的方法先求出答案,如果对于某个 \(c\),求出来的答案正好分成了 \(m\) 段,那么求出来的答案减去 \(ck\) 就是我们要求的。显然地,\(c\) 越大,段数越小,所以我们二分 \(c\),获得一个段数恰好为 \(m\) 的答案。如果对于 \(c=x\),段数小于 \(m\),对于 \(c=x+1\),段数大于 \(m\),那么取 \(c=x+1\) 时也存在分段数为 \(k\) 的做法,只需把答案减去 \((x+1)m\)。这样我们就得到了 \(O(n\log C)\) 的做法,很优!
点击查看代码
#include <cstdio>
#include <cstring>
#include <iostream>
#define db double
#define int long long
#define INF 1000000000000000000
using namespace std;
const int N = 1e5 + 5;
int n, m, a[N], s[N];
int f[N], g[N], q[N];
#define y(a) (f[a] + s[a] * s[a] - 2 * s[a])
#define x(a) (s[a])
long db K(int a, int b) { return (long db)(y(a) - y(b)) / (x(a) - x(b)); }
void calc(int mid)
{
memset(f, 0x3f, sizeof(f));
memset(g, 0, sizeof(g));
f[0] = 0, q[1] = 0;
int l = 1, r = 1;
for (int i = 1; i <= n; i++)
{
while (l < r && K(q[l], q[l + 1]) < 2 * s[i]) l++;
f[i] = f[q[l]] + (s[i] - s[q[l]] + 1) * (s[i] - s[q[l]] + 1) + mid;
g[i] = g[q[l]] + 1;
while (l < r && K(q[r - 1], q[r]) > K(q[r - 1], i)) --r;
q[++r] = i;
}
}
signed main()
{
cin.tie(0)->sync_with_stdio(false);
cout.tie(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i], s[i] = s[i - 1] + a[i];
int l = 0, r = INF, mid;
int ans;
while (l < r)
{
mid = (l + r) >> 1;
calc(mid);
if (g[n] <= m) ans = mid, r = mid;
else l = mid + 1;
}
calc(ans);
cout << f[n] - m * ans;
return 0;
}
现在为止我们得到的就是 wqs 在论文中展示的算法。我们尝试抽离出一个抽象的代数模型来描述 wqs 二分。
发现我们要解决的问题一般都是类似于区间分拆问题,其 2D-1D 递推式一般都类似:
其中 \(w(i,j)\) 满足四边形不等式。设 \(g(k):=f(n,k)\),那么有 \(g(k)\) 是凸函数。
\(\text{proof.}\)
尝试证明 \(g(k-1)+g(k+1)\geq 2g(k)\)。考虑一个长度为 \(k-1\) 的分拆 \([a_1,d_1],\dots,[a_{k-1},d_{k-1}]\) 与一个长度为 \(k+1\) 的分拆 \([b_1,c_1],\dots,[b_{k+1},c_{k+1}]\)。然后想着怎么去构造出两个长度为 \(k\) 的分拆,也就是把上面两个融合在一起,于是想到找一个最小的 \(j\) 满足 \(c_{j+1}\leq d_j\),由 \(d_{k-1}=n\) 可知其必然存在,随后根据其最小性,我们知道\(a_j<b_{j+1}\leq c_{j+1}\leq d_j\),于是从 \(j\) 把序列拆开,把两端末尾交换得到
于是 \(2g(k)\) 小于等于上面的区间的权值加一块,由四边形不等式可知 \(2g(k)\leq g(k-1)+g(k+1)\)。
\(\text{Q.E.D.}\)
我们维护了一个满足凸性的函数 \(f(x)\) 表示限制为 \(x\) 的时候的最优解。满足凸性意味着 \(f(x)\) 每一点上的切线斜率为单调的,也就是 \(f'(x)\) 单调(这里应该用 \(\Delta f(x)\)?)。我们考虑把我们的成本函数 \(w(i,j)\) 强行加参数,变成 \(w_c(i,j):=w(i,j)+c\),然后不管限制求最值,发现求出来的值关于 \(c\) 单调,由凸性易证,于是二分 \(c\),找到对应的 \(c\) 然后减去 \(cm\) 就是答案。
这样我们得到的就不仅仅是一个需要注意力的东西了,而是一个可以推出来的算法,并且可以灵活推广。剩下的就是做题了。