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\),则有转移

\[f_i=\min_{j=1}^{i-1}\{f_i+|s_i-s_j+i-j-L-1|^{P}\} \]

想要优化就只能往决策单调性上想了。打个表发现 \(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\),有转移

\[f_{i}=\min_{j=1}^{i-1}\{f_j+(s_i-s_{j})^2+c\} \\ = \min\{f_{j}+s_i^2-2s_is_j+s_j^2+c\} \\ = \min\{-2s_j\times s_i +s_j^2+f_j\}+s_i^2+c \]

在李超树上维护 \(-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 递推式一般都类似:

\[f_{k,i}=\operatorname{opt}\{f_{k-1,j}+w(j+1,i)\} \]

其中 \(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\) 把序列拆开,把两端末尾交换得到

\[[a_1,d_1],\dots, [a_{j-1},d_{j-1}],[a_j,c_{j+1}],[b_{j+2},c_{j+2}],\dots,[b_{k+1},c_{k+1}] \iff [b_1,c_1],\dots ,[b_{j+1},d_j],[a_{j+1},d_{j+1}],\dots,[a_{k-1},d_{k-1}] \]

于是 \(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\) 就是答案。

这样我们得到的就不仅仅是一个需要注意力的东西了,而是一个可以推出来的算法,并且可以灵活推广。剩下的就是做题了。

posted @ 2025-03-28 20:31  lhc0707  阅读(18)  评论(0)    收藏  举报