树形 DP 讲课笔记

· · 算法·理论

题单:https://www.luogu.com.cn/training/1034084

::::info[P3621 [APIO2007] 风铃]

:::success[Hint 1 (6 min)]

考虑一个无解情况。

若一个子树的左右子树均有深浅两种情况,则其无解。

:::

:::success[Solution (15 min)]

考察知识点:DFS 求解树形 DP。

不难发现,若所有叶子均一样深则输出 0

若叶子深度的差 \geq 2 则一定无解,输出 -1

若一个子树的左右子树均有深浅两种情况,则无解,输出 -1

剩下的情况就好处理了。

考虑什么时候需要交换。

若左子树全为深,且右子树有深有浅,则一定交换。

若左子树有深有浅,且右子树全为浅,则一定交换。

否则一定不交换。

利用树形 DP 或 DFS 统计子树内的深浅叶子节点个数。

可以试着自行写代码。(15 min)

:::

::::

::::info[P2610 [ZJOI2012] 旅游]

:::success[Hint 1 (7 min)]

考虑建图。一步能到达的两个城市间建边。

:::

:::success[Hint 2 (11 min)]

发现图中恰有 n-3 条边,n-2 个城市。

于是这是一棵树。

:::

:::success[Hint 3 (14 min)]

任意一条树的路径都可以被一条直线段覆盖。

自行证明。

:::

:::success[Solution (18 min)]

考察知识点:DFS 求解树形 DP。

结合 Hint 1,2,3 的结论,我们相当于求出这棵树的直径长度。

求直径可以 DP,DFS 等。

建图过程使用 map 维护,若两个城市间存在一条共边则可以一步到达。

可以试着自行写代码。(20 min)

:::

::::

::::info[P16189 [COI 2018] Paprike 胡椒]

:::success[Hint 1 (8 min)]

考虑设一个 sum_u 表示最小刀数时 u 所在的联通块的最小值。

显然每次选取子树的时候,选的 sum_u 越多越好,贪心转移。

:::

:::success[Solution (13 min)]

考察知识点:贪心优化树形 DP。

dp_uu 为根的子树中所有花环辣度小于等于 k 时,切的最小刀数。

对子节点按 $sum$ 排序,一直取最小的直到再取超过 $k$。 剩下的都切一刀即可。 可以试着自行写代码。(15 min) ::: :::: ::::info[[P13680 [IAMOI R2] 未送出的花](https://www.luogu.com.cn/problem/P13680)] :::success[Hint 1 (8 min)] 整棵树盛开度的最优形态一定是一个大根堆。 那么我们可以知道,一个点 $u$ 的美丽值就是根到它的路径上第 $\lceil\frac{dep_u}{2}\rceil$ 个点的盛开度,$dep_u$ 表示深度。 ::: :::success[Hint 2 (14 min)] 对于每个点,我们可以 DP 求出它被作为美丽值的次数。 ::: :::success[Hint 3 (20 min)] 考虑求 $k$ 朵花的时候。 按照盛开度从大到小枚举所有的点,并且累加 $cnt$,直到 $cnt \ge k$ 的时候结束,答案就是最后一个点的盛开度。 观察上述过程,不难发现盛开度从大到小排序后,一段前缀盛开度对应的点构成一个包含 $1$ 号点的联通块。 ::: :::success[Hint 4 (29 min)] 可以转化为对于 $k\in[1,n]$,求一个包含 $1$ 号点的连通块,满足这个连通块的 $\sum cnt_i\ge k$,同时点数最少。 这个等价于求包含 $1$ 号点的,大小为 $1,2,\cdots,n$ 的联通块的 $cnt$ 之和最大能到多少。 这是可以树形 DP 的,如果要构成一个联通块,那么如果我们不选点 $u$ 那么 $u$ 的子树中的点都不能选。 一个有限制的背包问题。 考虑 DFS 序。 ::: :::success[Solution (40 min)] 考察知识点:贪心优化树形 DP。 考虑拍到 DFS 序上,再把整个 DFS 序列翻转过来,那么第 $i$ 个点的子树区间就是 $[i-siz_{id_i}+1,i]$,其中 $id_i=j$ 表示 DFS 序为 $i$ 的点是 $j$,$siz$ 表示子树大小。 那么设 $dp_{i,j}$ 表示枚举到第 $i$ 个点,选了 $j$ 个,$cnt$ 之和的最大值。 - 如果选 $i$,那么 $dp_{i,j}=\max(dp_{i,j},dp_{i-1,j-1}+cnt_{id_i})$。 - 否则 $dp_{i,j}=\max(dp_{i,j},dp_{i-siz_{id_i},j})$。 就做完了。 可以先不用写代码。 ::: :::: --- ::::info[[P12136 [蓝桥杯 2025 省 B] 生产车间](https://www.luogu.com.cn/problem/P12136)] :::success[Hint 1 (5 min)] 每个节点有容量限制,转化成背包的形式。 ::: :::success[Hint 2 (11 min)] 考虑树形 DP,由于每个节点提供的值不是越多越好,需要进行分组背包。 ::: :::success[Solution (18 min)] 设 $dp_{i,j}$ 表示节点 $i$ 的权值能否恰好达到 $j$,初始叶节点只有 $dp_{u,w_u}$ 为真,其他节点 $dp_{u,0}$ 为真。 从下往上进行 DP,对于非叶节点 $u$,枚举 $u$ 的儿子 $v$,对于每个 $i,j(i+j\le w_u,j\le w_v)$,若 $dp_{v,j}$ 为真且 $dp_{u,i}$ 为真,则 $dp_{u,i+j}$ 为真。 输出节点 $1$ 最大能达到的权值即可。 可以试着自行写一下。(20 min) ::: :::: ::::info[[P14150 不动鸣神,恒常乐土](https://www.luogu.com.cn/problem/P14150)] :::success[Hint 1 (2 min)] 图是森林。 ::: :::success[Hint 2 (8 min)] 注意到 $k \le 10$,考虑放进 DP 状态。 ::: :::success[Hint 3 (20 min)] 直接设 $f_{i,j}$ 表示没选第 $i$ 个节点而选了 $j$ 个儿子的答案,$g_i$ 表示选了第 $i$ 个节点的答案。 $g_i$ 转移是朴素的。考虑 $f_{i,j}$ 的转移。 一定是按某个式子的大小排序后选点。 ::: :::success[Solution (35 min)] 考察知识点:贪心优化树形 DP / 树上背包。 朴素做法: - 首先选了自己的答案直接从下面没选的答案里更新,有 $g_i=a_i+\displaystyle\sum_{v\in son(i)}\max_{j\in[0,k-1]}f_{v,j}$。 - 然后考虑没选自己的答案: - 首先儿子也没选的答案和选了自己的答案的更新方式类似,直接从下面没选的答案里更新,有 $g_i=a_i+\displaystyle\sum_{v\in son(i)}\max_{j\in[0,k]}f_{v,j}

树上背包做法:

考虑分当前儿子选不选,选就加上 f_{v,k-1},不选就加上 f_{v,k}

然后树上背包即可。

对每棵树分别计算答案然后加起来。

可以先不用写代码。

:::

::::

::::info[P2515 [HAOI2010] 软件安装]

:::success[Hint 1 (6 min)]

发现图是一个基环树。中间的点环状依赖,要么都选要么都不选,可以缩点。

:::

:::success[Hint 2 (15 min)]

考虑建树,建一个超级原点 0 连接所有没有依赖的点。

然后树形 DP。

:::

:::success[Solution (25 min)]

考察知识点:缩点,树上背包。

考虑树上背包。

f_{i,j} 表示 i 的子树内用不超过 j 的空间的最大价值。

DP 转移是朴素的。

可以先不用写代码。

:::

::::

::::info[P11501 [ROIR 2019] 探险队 (Day 2) & P2607 [ZJOI2008] 骑士]

:::success[Hint 1 (5 min)]

发现这是一棵基环树,考虑分开环与树 DP。

:::

:::success[Hint 2 (8 min)]

对于每个环上的点,考虑其子树的最大贡献。

在子树内 DP。

:::

:::success[Hint 3 (12 min)]

考虑只有环,每个点带权怎么做。

:::

:::success[Solution (18 min)]

考察知识点:基环树 DP(树形 DP + 环形 DP)。

对每个子树 DP。

f_{u,0} 表示不选这个点的最大答案,f_{u,1} 表示选了这个点。

朴素 DP 转移到环上。

环上的 DP 也是朴素的。

可以先不用写代码。

:::

::::

::::info[P3478 [POI 2008] STA-Station]

:::success[Hint 1 (8 min)]

dp_u 表示当点 u 作为根时的深度之和。

考虑当根从一个点变成它的儿子时其他节点的深度如何变化。

:::

:::success[Solution (15 min)]

考察知识点:换根 DP。

当根从 u 变为 u 的子节点 v 时,考虑答案发生的变化。

不难发现,发现在儿子的子树外的点深度会增加 1,子树内的点深度会减 1

那么就有 dp_v=dp_u+(n-siz_v)-siz_v

其中 siz_xx 的子树大小。

先钦定节点 1 为根,求出 dp_1 和每个节点的 siz,然后换根转移即可。

可以试着自行写一下。(15 min)

:::

::::

::::info[P6554 Promises I Can't Keep]

:::success[Hint 1 (4 min)]

注意是等概率流向叶子不是等概率流向儿子。

:::

:::success[Hint 2 (14 min)]

发现可以变成根到每个叶子路径权和除以叶子数量,如果当前根是叶子需要额外减 1。

:::

:::success[Hint 3 (25 min)]

考虑换根 DP,以 1 为根的答案是容易求的,只需要处理每个点子树内叶子个数,乘上点权加和,同时对叶子打标记。

:::

:::success[Solution (35 min)]

考察知识点:换根 DP。

转移时,设当前节点为 v,父亲为 u,若 v 不是叶子,f_v=f_u-cnt_v \times a_u+(cnt_1-cnt_v) \times a_v,其中 cnt 是叶子个数,a 是点权。如果是,f_v=f_u-a_u+(siz_1-1)\times a_v-a_v

可以试着自行写一下。(20 min)

:::

::::

::::info[P2458 [SDOI2006] 保安站岗]

:::success[Hint 1 (8 min)]

考虑在 DP 里维护这个点被覆盖的状态。

由父亲,儿子,自身覆盖有三种情况。

:::

:::success[Solution (13 min)]

dp_{u,0/1/2} 表示 u 节点被覆盖的状态。

vu 的一个子节点,考虑转移。

$dp_{u,1}=\sum \min(dp_{v,1},dp_{v,2})+ \min({dp_{v,2}})$。 $dp_{u,2}=\sum \min(dp_{v,0},dp_{v,1},dp_{v,2}) + r_u$。 可以试着自行写代码。(15 min) ::: :::: ::::info[[P3047 [USACO12FEB] Nearby Cows G](https://www.luogu.com.cn/problem/P3047)] :::success[Hint 1 (10 min)] 分开考虑一个节点 经过其父亲 / 不经过其父亲 距离为 $k$ 的点。 ::: :::success[Solution (25 min)] 设 $f_{u,k}$ 表示在 $u$ 子树内离节点 $u$ 距离恰好是 $k$ 的点的个数,$g_{u,k}$ 是总个数。 $f$ 的求解是朴素的。通过 $f$ 推 $g$,$g_{v,k}$ 加上 $g_{u,k-1} - f_{v,k-2}$ 来容斥,其中 $u$ 是 $v$ 的父节点。 可以试着自行写一下。(15 min) ::::