Loading

动态dp

动态DP

基础概念

从一道简单的问题说起

  • 有一个长度为 \(n\) 的序列 \(a_i\),每个数可以选或者不选,但相邻两个数不能同时选,最大化选出的数的和。

有一个简单的 \(dp\),设 \(f_{i,0/1}\) 表示前 \(i\) 个数,第 \(i\) 个数是否选了的最大价值,转移时

  • \(f_{i,1}=f_{i-1,0}+a_i\)
  • \(f_{i,0}=\max(f_{i-1,0},f_{i-1,1})\)

现在把这个问题加强一下:

  • 有一个长度为 \(n\) 的序列 \(a_i\),每个数可以选或者不选,但相邻两个数不能同时选,最大化选出的数的和,需要支持单点修改和对区间查询。

我们可以把转移写成矩阵的形式:

\[\begin{bmatrix} f_{i,0}&f_{i,1}\\ \end{bmatrix} = \begin{bmatrix} f_{i-1,0}&f_{i-1,1}\\ \end{bmatrix} \times \begin{bmatrix} 0&a_i\\ 0&-\infty\\ \end{bmatrix} \]

其中矩阵乘法定义为 \((\max,+)\) 乘法。

不妨令

\[M_i= \begin{bmatrix} 0&a_i\\ 0&-\infty\\ \end{bmatrix} \]

那么答案可以写成

\[\begin{bmatrix} 0&0\\ \end{bmatrix} \times \prod\limits_{i=1}^nM_i \]

现在是单点修改,区间查询,我们可以用线段树维护区间内的 \(M_i\) 的乘积,因为矩阵乘法有结合律所以这样做是正确的。

这样子就可以在 \(\Theta(n\log n)\) 复杂度内解决这个问题。

类似上面的过程,在解决动态,范围查询的动态规划问题时,用数据结构维护矩阵乘法的方式也被称为动态 dp,也可以简称为 ddp。

练习 [ABC246Ex] 01? Queries

练习 CF750E New Year and Old Subsequence

树上的情况(静态)

P5024 [NOIP2018 提高组] 保卫王国

\(f_{x,0/1}\) 表示 \(x\) 子树内 , \(x\) 是否选了的最小代价,转移是显然的。

同时换根 dp 求出 \(g_{x,0/1}\) 代表 \(x\) 子树外,\(x\) 是否选了的最小代价。

询问时,把所有点拆成 \(a\) 子树内,\(b\) 子树内,\(lca(a,b)\) 的子树外,\(a\to b\) 的路径上。

前三者分别是 \(f_a,f_b,g_{lca(a,b)}\),对于后者,我们考虑倍增,设 \(F_{x,k,0/1,0/1}\) 表示 \(x\)\(x\)\(2^k\) 级祖先这段范围内(不算 \(x\) 子树,但是算路径上其他点的子树),\(x\) 是否选了,\(x\)\(2^k\) 级祖先是否选了的最小代价。

那么询问时倍增出一段链上的 \(F\) 即可,复杂度 \(\Theta((n+m)\log n)\)

练习 P8820 [CSP-S 2022] 数据传输

树上的情况(动态)

P4719 【模板】"动态 DP"&动态树分治

没有修改时,设 \(f_{x,0/1}\) 表示 \(x\) 子树内,节点 \(x\) 是否选了的最大权值和,转移是显然的:

  • \(f_{x,0}=\sum\limits_{y\in son_x}\max(f_{y,0},f_{y,1})\)
  • \(f_{x,1}=\sum\limits_{y\in son_x}f_{y,0}+a_x\)

但是树的结构不太好维护信息,因此考虑做一下轻重链剖分,设 \(h_x\)\(x\) 的重儿子。

现在考虑把每个点的 dp 写成关于其重儿子信息的矩阵,轻儿子的信息暴力维护。

对于轻儿子,维护

\(w_{x,0}=\sum\limits_{y\in son_x\& y\neq h_x}\max(f_{y,0},f_{y,1})\)

\(w_{x,1}=\sum\limits_{y\in son_x\& y\neq h_x}f_{y,0}\)

那么每条重链上的转移可以写成:

\[\begin{bmatrix} f_{x,0}&f_{x,1}\\ \end{bmatrix} = \begin{bmatrix} f_{h_x,0}&f_{h_x,1}\\ \end{bmatrix} \times \begin{bmatrix} w_{x,0}&w_{x,1}+a_x\\ w_{x,0}&-\infty\\ \end{bmatrix} \]

此时就可以维护每条重链的乘积了。

单点修改时,注意到只有 \(x\) 到根这条路径上的点的信息需要修改,并且 \(w\) 会发生变化的点一定只有每条重链的链顶的父亲,一共不超过 \(\Theta(\log n)\) 个点,每次修改过 \(w\) 之后重新更新一下所在重链的线段树的区间矩阵,再修改该重链链顶父亲的 \(w\),以此类推,需要修改 \(\Theta(\log n)\) 次线段树,因此复杂度为 \(\Theta(m\log^2 n)\)

练习 P6021 洪水

全局平衡二叉树

全局平衡二叉树是一个可以将普通树剖套线段树变成 1log 的东西。

还是轻重链剖分,对于每条重链,建立一棵特殊的线段树。

\(s_i\)\(i\) 的轻子树大小,每次选取 \(s\) 的带权中点作为分界线递归。

考虑复杂度,定义一个线段树节点的重量为区间内所有点的轻子树大小和,定义一个线段树叶子的重量为轻子树的大小和,容易发现每次跳父亲重量倍增,因此只会跳 \(\Theta(\log n)\) 次。

P4751 【模板】"动态DP"&动态树分治(加强版)

进阶题目

CF1286D LCC

考虑最先碰撞的一定是相邻两个粒子,具体的,可以相遇,向左追击,向右追击三种。

把所有碰撞所需时间排序,枚举最先碰撞的是哪种情况,那么就是要求时间更小的碰撞都不发生的概率。

相当于有若干限制,形如 \(i,i+1\) 不能同时选 \(x\)\(y\),记为 \(ok_{i,x,y}=0\)

那么考虑依旧维护矩阵乘积,转移可以写成

\[\begin{bmatrix} f_{i,0}&f_{i,1}\\ \end{bmatrix} = \begin{bmatrix} f_{i-1,0}&f_{i-1,1}\\ \end{bmatrix} \times \begin{bmatrix} p_{i,0}\times ok_{i-1,0,0}&p_{i,1}\times ok_{i-1,0,1}\\ p_{i,0}\times ok_{i-1,1,0}&p_{i,1}\times ok_{i-1,1,1}\\ \end{bmatrix} \]

单点更新,全局查询,写一个线段树即可。

复杂度 \(\Theta(n\log n)\)

P4426 [HNOI/AHOI2018] 毒瘤

题目保证了 \(m-n\leq 10\),也就是说,如果我们找出一颗生成树,那么只会有不超过 \(11\) 条非树边。

考虑容斥,\(2^{m-(n-1)}\) 枚举每条非树边是否被钦定,对于每条非树边,钦定它连着的两个点同时被选,那么现在问题就是,对于一棵树,每次钦定两个点必须选,或者取消钦定,求这棵树的独立集个数。

用树剖/全局平衡二叉树维护动态 dp 即可。

CF573D Bear and Cavalry

P6666 [清华集训2016] 数据交互

ZJOI2022 深搜

posted @ 2024-09-23 16:44  Larunatrecy  阅读(72)  评论(2编辑  收藏  举报