Commit 341d42bb authored by ljfcnyali's avatar ljfcnyali
Browse files

新增动态DP

parent 1ea65494
Loading
Loading
Loading
Loading
+34 −31
Original line number Diff line number Diff line
在学习本章前请确认你已经学习了 [矩阵](../math/matrix.md)  [树链剖分](../graph/hld.md) 
在学习本章前请确认你已经学习了 [矩阵](../math/matrix.md)  [树链剖分](../graph/hld.md) 

动态 DP 问题是猫锟在 WC2018 讲的黑科技,一般用来解决树上的 DP 问题,同时支持点权(边权)修改操作。

@@ -13,65 +13,68 @@

#### 广义矩阵乘法

定义广义矩阵乘法 $A\times B=C$ 为
定义广义矩阵乘法 $A\times B=C$ 为

$$
C_{i,j}=max_{k=1}^{n}(A_{i,k}+B_{k,j})
C_{i,j}=\max_{k=1}^{n}(A_{i,k}+B_{k,j})
$$

相当于将普通的矩阵乘法中的乘变为加,加变为 $max$ 操作
相当于将普通的矩阵乘法中的乘变为加,加变为 $\max$ 操作

同时广义矩阵乘法满足结合律,所以可以使用矩阵快速幂
同时广义矩阵乘法满足结合律,所以可以使用矩阵快速幂

#### 不带修改操作

令 $f_{i,0}$ 表示不选择 $i$ 的最大答案, $f_{i,1}$ 表示选择 $i$ 的最大答案
令 $f_{i,0}$ 表示不选择 $i$ 的最大答案, $f_{i,1}$ 表示选择 $i$ 的最大答案

则有 DP 方程 $\begin{cases}f_{i,0}=\sum_{son}max(f_{son,0},f_{son,1})\\f_{i,1}=\sum_{son}f_{son,0}\end{cases}$ 

答案就是 $max(f_{root,0},f_{root,1})$ 
则有 DP 方程 :
$$
\begin{cases}f_{i,0}=\sum_{son}\max(f_{son,0},f_{son,1})\\f_{i,1}=\sum_{son}f_{son,0}\end{cases}
$$
答案就是 $\max(f_{root,0},f_{root,1})$ 。

#### 带修改操作

首先将这棵树进行树链剖分,假设有这样一条重链
首先将这棵树进行树链剖分,假设有这样一条重链

![](./images/dynamic.png)

设 $g_{i,0}$ 表示不选择 $i$ 且只允许选择 $i$ 的轻儿子所在子树的最大答案, $g_{i,1}$ 表示选择 $i$ 的最大答案, $son_i$ 表示 $i$ 的重儿子
设 $g_{i,0}$ 表示不选择 $i$ 且只允许选择 $i$ 的轻儿子所在子树的最大答案, $g_{i,1}$ 表示选择 $i$ 的最大答案, $son_i$ 表示 $i$ 的重儿子

假设我们已知 $g_{i,0/1}$ 那么有 DP 方程 $\begin{cases}f_{i,0}=g_{i,0}+max(f_{son_i,0},f_{son_i,1})\\f_{i,1}=g_{i,1}+f_{son_i,0}\end{cases}$ ,答案是 $max(f_{root,0},f_{root,1})$ 
假设我们已知 $g_{i,0/1}$ 那么有 DP 方程 :
$$
\begin{cases}f_{i,0}=g_{i,0}+\max(f_{son_i,0},f_{son_i,1})\\f_{i,1}=g_{i,1}+f_{son_i,0}\end{cases}
$$
答案是 $\max(f_{root,0},f_{root,1})$ 。

可以构造出矩阵
可以构造出矩阵

$$
\left[
\begin{matrix}
\begin{bmatrix}
g_{i,0} & g_{i,0}\\
g_{i,1} & -\infty
\end{matrix}
\right]\times 
\left[
\begin{matrix}
\end{bmatrix}\times 
\begin{bmatrix}
f_{son_i,0}\\f_{son_i,1}
\end{matrix}
\right]=
\left[
\begin{matrix}
\end{bmatrix}=
\begin{bmatrix}
f_{i,0}\\f_{i,1}
\end{matrix}
\right]
\end{bmatrix}
$$

注意,我们这里使用的是广义乘法规则
注意,我们这里使用的是广义乘法规则

可以发现,修改操作时只需要修改 $g_{i,1}$ 和每条往上的重链即可
可以发现,修改操作时只需要修改 $g_{i,1}$ 和每条往上的重链即可

#### 具体思路

-   DFS 预处理求出 $f_{i,0/1}$ 和 $g_{i,0/1}$ 
-   对这棵树进行树剖(注意,因为我们对一个点进行询问需要计算从该点到该点所在的重链末尾的区间矩阵乘,所以对于每一个点记录 $End_i$ 表示 $i$ 所在的重链末尾节点编号),每一条重链建立线段树,线段树维护 $g$ 矩阵和 $g$ 矩阵区间乘积
-   修改时首先修改 $g_{i,1}$ 和线段树中 $i$ 节点的矩阵,计算 $top_i$ 矩阵的变化量,修改到 $fa_{top_i}$ 矩阵
-   查询时就是 1 到其所在的重链末尾的区间乘,最后取一个 $max$ 即可
1.  DFS 预处理求出 $f_{i,0/1}$ 和 $g_{i,0/1}$ 。

2.  对这棵树进行树剖(注意,因为我们对一个点进行询问需要计算从该点到该点所在的重链末尾的区间矩阵乘,所以对于每一个点记录 $End_i$ 表示 $i$ 所在的重链末尾节点编号),每一条重链建立线段树,线段树维护 $g$ 矩阵和 $g$ 矩阵区间乘积。

3.  修改时首先修改 $g_{i,1}$ 和线段树中 $i$ 节点的矩阵,计算 $top_i$ 矩阵的变化量,修改到 $fa_{top_i}$ 矩阵。

4.  查询时就是 1 到其所在的重链末尾的区间乘,最后取一个 $\max$ 即可。

#### 代码