转存的 【学员分享】动态dp

题单介绍

先给大家道个歉: - 讲课人昨天感冒了,因此嗓子哑了,声音听起来有点不舒服 - 讲课人不会在 ppt 里插入 $\LaTeX$,因此选择了直接在 typora 里写好再截图到 ppt 里 的下策, 如果有需要的话,可以和我要 .md 文件 - 树上动态 dp 可以使用 “全局平衡二叉树” 做到 $1\log$,但是讲课人才疏学浅,只会树剖 $2\log$ 做法, 因此这个 ppt 中也只会介绍树剖做法 - 讲课人只是一个萌萌小朋友,如果哪里讲错了,还麻烦大佬指出 前置知识: - 线段树 - 既然是 “动态” 的 dp,那么肯定要支持修改操作 - 大家在上周的学习中已经学过了线段树,这里就不再赘述 - 矩阵乘法 - 本质其实是 dp 的转移过程 - 不会的同学可以去看一下 洛谷日报#108 https://shehuizhuyihao.blog.luogu.org/post-zhen-sheng-fa SP1043 GSS1 - Can you answer these queries I 题意简述:给出长度为 $n$ 的序列 $a$ 以及 $m$ 组询问,对每组询问 $[l_i,r_i]$ 回答区间内的最大子段和。 数据范围:$n,m\le 5\times 10^4$。 这题有一个经典做法,就是用线段树维护区间的最大前缀和、最大后缀和、总和、最大子段和再进行合并,但这并不是我们今天要讨论的。 考虑朴素的 dp 转移方程,设 $f_i$ 表示以 $i$ 结尾的最大子段和,$g_i$ 表示 $\max\limits_{j\le i} f_j$,有转移 $\left\{ \begin{array}{lr} f_i=\max(f_{i-1}+a_i,a_i) \\ g_i=\max(g_{i-1},f_i) \end{array} \right.$ 我们需要构造一个转移矩阵,使得 $\begin{bmatrix} f_{i-1} & g_{i-1} & \cdots\end{bmatrix}$ $\begin{bmatrix} a_i & \cdots \\ \vdots & \ddots \end{bmatrix}$ $=$ $\begin{bmatrix} f_i & g_i & \cdots\end{bmatrix}$。 此时你会发现,我们需要用到一个矩阵乘法里不涉及的运算:$\max$。 于是我们需要重定义矩阵乘法:$C_{i,j}=\max \limits_{k}A_{i,k}+B_{k,j}$。 这里也可以使用 $\min$,具体按照 dp 转移方程而定。 回到本题,我们可以将状态设为:$\begin{bmatrix} f_j & 0 & g_j \end{bmatrix}$,并构造出这样的转移矩阵:$\begin{bmatrix}a_i & -\infty & a_i \\ a_i & 0 & a_i \\ -\infty & -\infty & 0\end{bmatrix}$。 手动模拟一下转移过程,可以发现得到的新矩阵为:$\begin{bmatrix}\max\{f_j+a_i,0+a_i,g_j+-\infty\} & \max\{f_j+-\infty,0+0,g_j+-\infty\} & \max\{f_j+a_i,0+a_i,g_j+0\}\end{bmatrix}$,整理得: $\begin{bmatrix}\max\{f_j+a_i,a_i\} & 0 & \max\{f_j+a_i,a_i,g_j\}\end{bmatrix}$。 对照我们之前推出来的式子,我们惊喜的发现这个矩阵就是 $\begin{bmatrix}f_i & 0 & g_i\end{bmatrix}$。 不难发现,我们新定义的运算是满足结合律的(证明请自行百度),于是可以利用线段树快速求出一段区间的矩阵的乘积,从而进行转移。 再来分析一下时间复杂度,一次矩阵乘法的复杂度是 $O(a^3)$ 的,其中 $a$ 为矩阵的边长,线段树查询一段区间的信息是 $O(\log n)$ 的,故总时间复杂度为 $O((n+m)a^3\log n)$。 SP1716 GSS3 - Can you answer these queries III 题意简述:给出长度为 $n$ 的序列 $a$ 以及 $m$ 组操作,操作 `0 x y` 表示把 $a_x$ 修改为 $y$,操作 `1 l r` 表示询问区间 $[l,r]$ 的最大子段和。 数据范围:$n,m\le 5\times 10^4$。 可以发现其实就是 GSS1 多了个单点修改,那么只要在线段树上修改对应点的信息即可。 CF750E New Year and Old Subsequence 题意简述:给出长度为 $n$ 的序列 $a$ 以及 $m$ 组询问,对每组询问回答 $a$ 的子串 $[l_i,r_i]$ 至少删去多少个字符后才能使得其不包含子序列 2016 而包含 2017。 数据范围:$n,m\le 2\times 10^5$。 设 $f_{i,0/1/2/3/4}$ 分别表示当前子串末尾为 $i$ 时包含$ \varnothing/2/20/201/2017$ 最少需要删去多少个字符,那么有 $\left\{ \begin{array}{lr} f_{i,0}=f_{i-1,0}+[s_i=2] \\ f_{i,1}=\min(f_{i-1,1}+[s_i=0],f_{i-1,0}[s_i=2]) \\ f_{i,2}=\min(f_{i-1,2}+[s_i=1],f_{i-1,1}[s_i=0]) \\ f_{i,3}=\min(f_{i-1,3}+[s_i=7\ \lor\ s_i=6],f_{i-1,2}[s_i=1]) \\ f_{i,4}=\min(f_{i-1,4}+[s_i=6],f_{i-1,3}[s_i=7]) \end{array} \right.$ 构造出转移矩阵后进行动态 dp 即可。 一个 trick: 可以发现我们的答案矩阵其实只有一行,那么在线段树上查询的时候只用完成第一行的矩阵乘法。(注意是查询,在线段树 push_up 的时候还是得用正常的矩阵乘法)。 使用该 trick 后 966ms -> 607ms。优化还是较明显的。 CF573D Bear and Cavalry 题意简述:有 $n$ 个人和 $n$ 匹马,第 $i$ 个人对应第 $i$ 匹马,要求每个人都不能骑自己对应的马。第 $i$ 个人的能力值为 $w_i$,第 $i$ 匹马的能力值为 $h_i$,第 $i$ 个人骑第 $j$ 匹马的能力值为 $w_ih_j$,整个军队的总能力值为 $\sum w_ih_j$(一个人只能骑一匹马,一匹马只能被一个人骑)。有 $m$ 个操作,每次给出 $a,b$,交换 $a$ 和 $b$ 对应的马。每次操作后你都需要输出最大的总能力值。 数据范围:$n\le 3\times10^4$,$m\le 10^4$。 有这样的结论:我们把 $w,h$ 排序后,每个人 $i$ 匹配的马必定在 $[i-2, i+2]$ 中。 这里直接引用 @_sys 的证明: 于是设 $f_i$表示前 $i$ 个人和前 $i$ 匹马匹配完成的最大权值,则有转移 $f_i=\max\limits_{j\ge i-3}{f_j+calc(j+1,i)}$。 其中 $calc(l,r)$ 表示 $[l,r]$ 区间内满足每个人不骑自己对应的马的最大总能力值,可以 $O((r-l+1)!)$ 枚举每种情况计算。 最后构造出转移矩阵并进行动态 dp 即可。 P4719 【模板】"动态 DP"&动态树分治 题意简述:给定一棵 $n$ 个点的树,$i$ 号点的点权为 $a_i$。有 $m$ 次操作,每次操作给定 $x,y$,表示修改点 $x$ 的权值为 $y$。你需要在每次操作之后求出这棵树的最大权独立集的权值大小。 数据范围:$n,m\le 10^5$。 先考虑没有修改的情况下怎么做。首先先选取 1 号点作为全树的根。然后我们设 $f_{i,0}$ 表示不选择 $i$ 号点时,以 $i$ 号点为根的子树的最大权独立集;$f_{i,1}$ 表示选择 $i$ 号点时,以 $i$ 号点为根的子树的最大权独立集。则有如下转移方程: $\left\{ \begin{array}{lr} f_{i,0}=\sum\limits_{j\in son_i} \max(f_{j,0},f_{j,1}) \\ f_{i,1}=\sum\limits_{j\in son_i} f_{j,0} + a_i \end{array} \right.$ 答案就是 $\max(f_{1, 0}, f_{1, 1})$。 然后你熟练地准备构造转移矩阵,却发现无从下手:序列上的 dp 是 “一对一” 转移,而树上的 dp 是 “多对一” 转移,即要考虑到每个儿子。 那么如何把这个转化成 “一对一” 转移的形式呢?这就要用到树链剖分了。 设 $g_{i,0}$ 表示 $i$ 号点的所有轻儿子可取可不取,且不取 $i$ 的最大权独立集,$g_{i,1}$ 表示 $i$ 号点的所有轻儿子都不取,且取了 $i$ 的最大权独立集,$j$ 为 $i$ 的重儿子。则有如下转移方程: $\left\{ \begin{array}{lr} f_{i,0}=g_{i,0}+\max(f_{j,0},f_{j,1}) \\ f_{i,1}=g_{i,1}+ f_{j,0} \end{array} \right.$ 这样就在转移时就只需要考虑重儿子了。相应的转移矩阵也不难构造,请自行尝试。 那么问题来了:带修改操作,如何维护 $g$ 呢? 对于一条重链,求出这条重链上矩阵的乘积,这就是这条重链链头对它父亲的贡献(显然重链链头是他父亲的轻儿子),将父亲的 $g$ 减去贡献后,更新重链上需要修改的点的矩阵,再将新的乘积对父亲的 $g$ 的贡献求出并加上。完成这一切后,父亲成为新的需要修改的点,父亲所在重链成为新的需要处理贡献的重链,就这样处理下去,直到重链链头为根为止。 由于树剖带一个 $\log$,因此总时间复杂度为 $O(na^3 \log^2 n)$。 [NOIP2018 提高组] 保卫王国 题意简述:给定一棵 $n$ 个点的树,给 $i$ 号点染色的花费为 $a_i$,要求任2个相邻点至少有1个被染色。给出 $m$ 组询问,每次强制两个点的状态(染/不染),求出每次询问的最小花费。 数据范围:$n,m\le 10^5$。 设 $f_{i,0}$ 表示 $i$ 号点不染色的最小花费,$f_{i,1}$ 表示 $i$ 号点染色的最小花费,则有如下转移: $\left\{ \begin{array}{lr} f_{i,0}=\sum\limits_{j\in son_i} f_{j,1} \\ f_{i,1}=\sum\limits_{j\in son_i} \min(f_{j,0},f_{j,1})+a_i \end{array} \right.$ 于是按照与模板题相同的方法动态 dp 即可。 课后练习: - CF1609E William The Oblivious - 前两天的 CF,感觉大家都场切了( - CF1286D LCC - P3781 [SDOI2017]切树游戏 - 这题的 hack 数据卡树剖 /fn,大家可以去学一下 “全局平衡二叉树” 的黑科技。

题目列表

  • GSS1 - Can you answer these queries I
  • GSS3 - Can you answer these queries III
  • New Year and Old Subsequence
  • Bear and Cavalry
  • 【模板】动态 DP
  • [NOIP 2018 提高组] 保卫王国
  • William The Oblivious
  • LCC
  • [SDOI2017] 切树游戏