「学习笔记」整体 dp
适用范围
对于一些只有状态上的值不同而转移方程完全相同的 dp 可以考虑使用这个 trick。
一般用线段树合并维护,当前点 \(x\) 的线段树的叶子节点下标 \(y\) 的位置的权值,是第 \(y\) 次 dp 到 \(x\) 这里的权值。
例题
[NOIp 2018] 保卫王国
转移方程为:
\[\begin{aligned}
f_{x, 0} &= \sum_{y \in \mathrm{son}_x} f_{y, 1}\\
f_{x, 1} &= p_x + \sum_{y \in \mathrm{son}_x} \min(f_{y, 0}, f_{y, 1})
\end{aligned}
\]
用线段树合并维护 \(m\) 次询问,那么一个 a x b y
的询问相当于线段树上的单点修改,可以用矩阵维护。
在向父亲合并时需要乘上以下矩阵 \(\begin{bmatrix} \infty & 0\\0 & 0 \end{bmatrix}\) 以方便合并:
\[\begin{bmatrix} f_0 & f_1 \end{bmatrix} \times \begin{bmatrix} \infty & 0\\0 & 0 \end{bmatrix} = \begin{bmatrix} f_1 & \min(f_0, f_1) \end{bmatrix}
\]
代码要比正常的动态 dp 短很多。code
[九省联考 2018] 秘密袭击
前面的推导部分不再赘述。
用生成函数的方式表示出转移方程。
\[F_{i, j} = x ^ {[v_i \geq j]} \prod_{p = 1}^{|s|} (F_{s_p, j} + 1)
\]
先枚举一个数字 \(p\),设 \(f_{i, y}\) 表示 \(F_{i, y}(x)\) 这个生成函数在 \(x = p\) 时候的点值,为了方便枚举答案,设 \(g_{i, y}\) 表示 \(f_{i, y}\) 的子树和。
用线段树合并维护 \(f\) 和 \(g\),于是需要支持这两个操作:
- \(f\) 整体 \(+ 1\) 和整体乘上一个数。
- 将 \(f\) 加到 \(g\) 上。
考虑定义一个变换 \((a, b, c, d)\) 使得 \((f, g) \to (af + b, cf + d + g)\),那么两个变换 \((a_1, b_1, c_1, d_1)\) 和 \((a_2, b_2, c_2, d_2)\) 合并会变成 \((a_1a_2, a_2b_1 + b_2, c1 + c_2a_1, c_2b_1 + d_1 + d_2)\),这样就可以很好维护了,只需要最后再插值回来即可。
这个标记应该也可以用矩阵维护。code