动态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\),每个数可以选或者不选,但相邻两个数不能同时选,最大化选出的数的和,需要支持单点修改和对区间查询。
我们可以把转移写成矩阵的形式:
其中矩阵乘法定义为 \((\max,+)\) 乘法。
不妨令
那么答案可以写成
现在是单点修改,区间查询,我们可以用线段树维护区间内的 \(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}\)。
那么每条重链上的转移可以写成:
此时就可以维护每条重链的乘积了。
单点修改时,注意到只有 \(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\)。
那么考虑依旧维护矩阵乘积,转移可以写成
单点更新,全局查询,写一个线段树即可。
复杂度 \(\Theta(n\log n)\)。
P4426 [HNOI/AHOI2018] 毒瘤
题目保证了 \(m-n\leq 10\),也就是说,如果我们找出一颗生成树,那么只会有不超过 \(11\) 条非树边。
考虑容斥,\(2^{m-(n-1)}\) 枚举每条非树边是否被钦定,对于每条非树边,钦定它连着的两个点同时被选,那么现在问题就是,对于一棵树,每次钦定两个点必须选,或者取消钦定,求这棵树的独立集个数。
用树剖/全局平衡二叉树维护动态 dp 即可。