树形 dp 与 动态 dp
P1352 没有上司的舞会
对于有根树 \(T\),求其最大权独立集。\(n\leq 6\times 10^3\)。
设 \(f[i,0/1]\) 为选中/不选中 \(i\) 的最大收益。我们有
时间复杂度 \(\Theta(n)\)。
树上背包
树上背包是分组背包的模型。
具体地,对于 \(u\) 的每个儿子 \(v\),我们将其看成一组,每组只能选一个物品。
分组背包枚举顺序为
- 组
- 质量
- 每组中的物品
注意倒序枚举质量。可以证明时间复杂度是 \(\Theta(nm)\) 的。
对于有依赖的背包问题,可以利用 dfs 序来优化。
P2015 二叉苹果树
树背包模板题。
不妨设 \(f[u,k]\) 为在 \(u\) 的子树内保留 \(k\) 根树枝,能保留的最多苹果数。
对于 \(u\),我们将每一个孩子 \(v\) 的每个状态 \(f[v,w]\) 看作一个质量为 \(\boldsymbol{w+1}\),价值为 \(f[v,w]\) 的物品。于是有
P1273 有线电视网
给定以 \(1\) 为根的树 \(T\),边有权。额外地,叶子也有点权。
可以任意割断一些边。考虑 \(1\) 所在的连通块 \(T'=(V',E')\)。我们定义收益 \(w\) 为 \(V'\) 中的叶子的权值和减去 \(E'\) 的边权和。
在 \(w\geq 0\) 的前提下,求出连通块中叶子的最大数量。
\(N\leq 3\times 10^3\),\(w_i,val_i\leq 10\)。
我们考虑 \(f[u,k]\) 表示在 \(u\) 所在子树中叶子数量为 \(k\) 的时候,收益的最大值。
对于叶子,我们有 \(f[u,1]=val_u\)。
然后就是树背包。\(f[u,k]\gets \max(f[u,k-j],f[v,j]-w)\)
时间复杂度 \(\Theta(nm)\)。
P4322 [JSOI2016] 最佳团体
考虑一棵以 \(1\) 为根的有根树。可以选中 \(k\) 个节点,但是必须满足:
- 若 \(i\) 被选中,则 \(fa_i\) 被选中
额外地,每个节点有两个属性 \(a_i\) 和 \(b_i\)。设选中节点构成集合 \(S\),最小化
套路地考虑二分。设当前二分到 \(mid\)。
\(mid\gt val\iff mid\sum b_v\gt \sum a_v \iff \sum (a_v-b_v)\lt 0\)
于是我们设当前 \(u\) 的点权 \(val_u=a_u-b_u\)。
设 \(f[u,k]\) 表示选中 \(u\),且在 \(u\) 个子树内选中 \(k\) 个点的点权最大值。于是就是一个树形背包。
起初 \(f[u]=-\infty\)。若 \(f[1]\lt 0\),则令 \(r \gets mid\);否则令 \(l\gets mid\)。
时间复杂度 \(\Theta(n^2\log V)\)。
P3047 [USACO12FEB] Nearby Cows G
对于 \(n\) 个点的树,记
对 \(\forall u\in[1,n]\),求出 \(f(u)\)。\(n\leq 10^5\),\(k\leq 20\)。
不妨设 \(f[i,j]\) 为距离 \(i\) 为 \(j\) 的节点个数。
我们考虑两次 dfs。不妨钦定 \(1\) 为根。
对于第一次 dfs,不妨求出子树的贡献。于是
对于第二次 dfs,不妨加上子树外的贡献。于是
显然这样会多算某些不合法的点。我们考虑会多算哪些点。
不难发现是 \(u\) 子树内距离 \(u\) 为 \((i-2)\) 的点。于是我们直接减去即可。
时间复杂度 \(\Theta(nk)\)
P2585 [ZJOI2006] 三色二叉树
[2024.2.15 模拟赛 B] 树上问题
给定一个叶向树形图,其中 \(1\) 为根,点有点权。我们把给定的边称为“原始边”。
考虑如下操作:选中一条 \(u\to v\) 的路径,满足:
- 路径只经过“原始边”。
然后删除路径上所有的边,连边 \((u,v)\)。
我们设 \(\operatorname{path}(u,v)\) 表示是否存在 \(u\to v\) 的路径。
求出
\(n\leq 2\times 10^5\),\(1\leq a_i\leq 10^6\)。
P3698 [CQOI2017] 小Q的棋盘
给定一棵树。求从节点 \(1\) 出发走 \(m\) 步最多能经过多少点。重复经过只计一次。
\(n,m\leq 100\)。
(事实上本题是 POJ 2486 的点权为 \(1\) 的版本)
不妨设 \(f[u,k]\) 表示从节点 \(u\) 的子树出发,走 \(k\) 步能经过多少节点;设 \(g[u,k]\) 表示从节点 \(u\) 的子树出发,走 \(k\) 步并且回到 \(u\) 最多经过多少节点。
于是 \(g\) 的转移是朴素的背包,即
然后你发现 \(f\) 是没那么朴素的背包,即
时间复杂度 \(\Theta(nm)\)。
P5658 [CSP-S2019] 括号树
有一棵以 \(1\) 为根的有根树。额外地,每个节点上有一个 \(\texttt{(}\) 或 \(\texttt{)}\)。对于 \(\forall i\in [1,n]\),求出 \(f(i)\)。
\(f(i)\) 定义为 \(1\to i\) 的路径上经过节点的上的括号组成的括号序列中合法的子串数。\(n\leq 5\times 10^5\)。
考虑对于序列怎么做。问题化为:
给定括号序列 \(S\),对于每一个 \(i\in [1,|S|]\),求出 \(S[1..i]\) 中合法的子串数量。
设 \(f[i]\) 为 \(S\) 中以 \(i\) 结尾的合法的子串数量。我们考虑在右括号处计算贡献。
设右括号 \(i\) 的匹配的左括号位置为 \(l_i\)(若有)。于是有 \(f[i]=f[l_i-1]+1\)。
于是对于每一个 \(i\),答案为 \(\sum_{k\leq i} f[i]\)。
我们考虑树上做法。我们对节点 \(i\)(右括号),设其匹配的左括号为 \(pre_i\),点 \(i\) 的父亲为 \(fa_i\),于是我们有
然后维护一下就好了。
需要注意的是,如果在进入 \(i\) 的时候弹掉了 \(pre_i\)(配对的左括号),出 \(i\) 的时候要把 \(pre_i\) 压回来。
[2024.2.16]
有一棵 \(n\) 个节点的树,点 \(i\) 有颜色 \(c_i\)。
求有多少个联通块满足:连通块内存在一种颜色,使得这种颜色(在联通块内)的数量严格大于联通块大小的一半。
\(n,c_i\leq 3\times 10^3\)。
POJ1848 Tree
对于一棵 \(n\) 个节点的树,添加最少的边使得每一个节点都在恰好一个环中,或报告无解。
P3354 [IOI2005] Riv 河流
有一棵 \(n\) 个点的根向树形图,其中 \(1\) 号点为特殊点,边有边权,点 \(i\) 有点权 \(v_i\)。记 \(i\) 到最近的特殊点距离为 \(d_i\)。
现在需要再选择 \(k\) 个节点作为特殊点,最小化 \(\sum_i d_iv_i\)。
P3177 [HAOI2015] 树上染色
给定一棵 \(n\) 的节点的树,边有边权。给定 \(k\),在这棵树中选择 \(k\) 个点染成黑色,其他点是白色。收益定义为黑点两两之间的距离加上白点两两之间的距离的和。求收益最大值。
\(n,k\leq 2\times 10^3\)。
不妨设 \(f[u,i]\) 为 \(u\) 子树选 \(i\) 个黑点的收益。考虑边 \((u,v,w)\) 的贡献。
不妨设 \(v\) 子树内有 \(x\) 个黑点,\(u\) 未加入 \(v\) 时有 \(y\) 个白点,大小为 \(siz_u\)。于是收益为
于是我们有
然后就做完了,时间复杂度 \(\Theta(nk)\)。
P4395 [BOI2003] Gem 气垫车
朴素的贪心是错的,可以构造数据卡掉。
设 \(f[u,j]\) 为 \(u\) 选正整数 \(j\) 的最小权值,显然有
这样设 \(j\) 值域为 \([1,m]\),时间复杂度是 \(\Theta(nm)\) 的。
不难想到 \(m\) 不会很大,于是随便挑一个数就过了。可以证明 \(m=\mathcal{O}(\log n)\)。
P4516 [JSOI2018] 潜入行动
考虑一棵 \(n\) 个点的无根树 \(T=(V,E)\)。选择监听点集 \(S\subseteq V\),则被监听点集 \(f(S)=\{u:u\in V, \exists v\in S, \mathrm{dis}(u,v)=1\}\),即所有距离 \(S\) 中点距离为 \(1\) 的点。
求出满足以下条件的监听点集 \(S\) 的个数:
- \(|S|=k\)
- \(f(S)=V\)
对 \((10^9+7)\) 取模。
\(n\leq 10^5\),\(k\leq 10^2\)。
不妨设 \(f[u,k,0/1,0/1]\) 为在 \(u\) 的子树内选择 \(k\) 个监听点,是否选择根,根是否被监听(但除了根之外的节点一定被监听了)的方案数。
起初对于任意节点,\(f[u,0,0,0]=1\),\(f[u,1,1,0]=1\)。
考虑转移。如果 \(v\) 没有被监听,则 \(u\) 一定要选择。
如果 \(v\) 被选择了,则 \(u\) 一定被监听。
也就是
如果不选 \(u\),那么 \(v\) 一定已经被监听了。那么
答案为 \(f[1,k,0,1]+f[1,k,1,1]\)。
注意本题转移的时候为了避免重复转移或者错误转移,可以开一个临时数组先存下之前的数据。
CF1929D Sasha and a Walk in the City
给定 \(n\) 个节点的树 \(T\)。求满足以下条件的点集 \(V\) 的数量:
- 不存在一条路径,经过 \(V\) 中的三个或以上的点。
对 \(998,422,353\) 取模。
不妨钦定一个根。我们设 \(f[u]\) 表示在 \(u\) 子树内选中非空点集 \(S\),使得 \(S\) 中不存在两个节点有祖先关系的方案数量。
显然有
可以选中 \(f[v]\) 中的空集,所以要加 \(1\)。
答案即为
其中 \(1\) 为空集的贡献。时间复杂度 \(\Theta(n)\)。
[NOIP2022] 建造军营
给定无向连通图 \(G=(V,E)\)。求选中非空点集 \(V'\subseteq V\) 和 边集 \(E'\subseteq E\) 的方案数,满足以下条件:
- 删去 \(E\backslash E'\) 的任意一条边后,\(V'\) 内的节点都两两可达。
对 \((10^9+7)\) 取模。\(n\leq 5\times 10^5\),\(m\leq 10^6\)。
不难发现 E-DCC 内的边是任取的,同一个 E-DCC 内的点也可以随意选择,于是我们考虑对 E-DCC 缩点。
显然缩点后形成一棵树。对于每个 E-DCC \(u\),记其内部点数为 \(V_u\),边数为 \(E_u\)。那么
-
选中它,有 \(2^{E_u}\) 种方案。
-
不选中它,有 \(2^{E_u}(2^{V_u}-1)=2^{E_u+V_u}-2^{E_u}\) 种方案。
那么问题转化为:
给定树 \(T=(V,E)\)。求选中非空点集 \(V'\subseteq V\) 和 边集 \(E'\subseteq E\) 的方案数,满足以下条件:
- 删去 \(E\backslash E'\) 的任意一条边后,\(V'\) 内的节点都两两可达。
额外地,选中 \(u\) 有 \(A_u\) 种方法,不选中有 \(B_u\) 种方法。
钦定 \(1\) 为根节点。朴素地,我们想到设 \(f[u,0/1]\) 为 \(u\) 子树内有/没有节点被选中的方案数。
发现信息过少,有以下思路;
- 增加状态;
- 添加限制。
为了防止算重,我们钦定考虑 \(f[u]\) 时,子树 \(u\) 以外的部分没有被选择的节点。记 \(s(u)\) 为以 \(u\) 为根的子树中的边数,这样点 \(u\) 对答案的贡献即为
(注意,\(u\to \mathrm{fa}(u)\) 的边不可以被保护,因为这样会算重)
特别地,对于点 \(1\),答案为 \(f[1,1]\)。
我们发现,\((u,v)\) 必须选中,当且仅当:
- \(v\) 中有节点被选中,且 \(u\) 之前已经有节点被选中了
否则,是否看守 \((u,v)\) 是任意的。
我们显然有
起初,\(f[u,0]=2^{E_u}\),\(f[u,1]=2^{E_u+V_u}-2^{E_u}\)。时间复杂度 \(\Theta(n)\)。
CF1929E Sasha and the Happy Tree Cutting
给定 \(n\) 个点的树 \(T=(V,E)\),以及 \(k\) 对节点 \((u_i,v_i)\)。
试最小化边集 \(E'\) 的大小,满足:
- \(E'\subseteq E\);
- \(\forall i\in [1,k]\),\(u\to v\) 的简单路径上存在一条 \(E'\) 中的边。
\(n\leq 10^5\),\(k\leq 20\)。
钦定根为 \(1\)。不妨设 \(f[u,S]\) 为子树 \(u\) 中满足的节点对集合为 \(S\) 时的最小边数。显然答案为 \(f[1,U]\)。
对于 \(u\) 子树中合并 \(v\),我们可以枚举是否选择边 \((u,v)\)。设路径上存在边 \((u,v)\) 的点对集合为 \(E\)。
于是有
时间复杂度 \(\Theta(n4^k)\),无法通过。我们应该换个思路。
事实:设路径中存在边 \(i=(u,v)\) 的点对集为 \(S_i\)。本质不同的 \(S_i\) 只有 \(\Theta(k)\) 种。
证明:考虑构造关于这些点对的虚树,显然虚树中只有 \(\Theta(k)\) 个节点,故只有 \(\Theta(k)\) 条边。
然后你发现就做完了。不难 \(\Theta(nk)\) 预处理出 \(S_i\),然后套路地设 \(f[S]\) 为满足了点对 \(S\) 的方案数,\(h_i\) 为 \(S_i\) 的方案数。于是有
时间复杂度 \(\Theta(nk+k2^k)\)。
启示:本题对于本质不同的 \(S_i\) 只有 \(\Theta(k)\) 种的证明运用到了虚树的性质。当关键点 \(k\) 较小的时候,考虑虚树。
[ABC152F] Tree and Constraints
给定 \(n\) 个点的树 \(T=(V,E)\),以及 \(k\) 对节点 \((u_i,v_i)\)。
求选出 \(E'\) 的方案数,满足:
- \(E'\subseteq E\);
- \(\forall i\in [1,k]\),\(u\to v\) 的简单路径上存在一条 \(E'\) 中的边。
\(n\leq 50\),\(k\leq \min(20,{n\choose 2})\)。
\(\Theta(nk)\) 预处理出每条边被覆盖的集合,然后套路地设 \(f[S]\) 为满足了点对 \(S\) 的方案数。
然后你会发现这是一个背包。
时间复杂度 \(\Theta(nk+n2^k)\)。
[HEOI 2013] SAO
考虑树形 dp。
我们设 \(f[i,j]\) 为 \(i\) 在其子树内排名为 \(j\) 的方案数。
然后我们要加入 \(u\) 的子树 \(v\)。
设合并后 \(u\) 的排名为 \(k\),枚举 \(u\) 原来的排名 \(p\) 和 \(v\) 的排名 \(q\)。
- \(v\to u\)(\(u\gt v\))
显然我们需要从 \(v\) 中补 \((k-p)\) 个数进 \(u\) 中。由于 \(u\gt v\),必须有 \(q\leq k-p\iff p+q\leq k\)。
于是我们需要:
-
取出 \(v\) 中的前 \((k-p)\) 个数按顺序插入 \(u\) 的前 \(p-1\) 个数所形成的 \(p\) 个空中。
-
将剩下的 \(siz_v+p-k\) 个数按顺序插入 \(u\) 的后 \(siz_u-p+1\) 个空中。
以上允许空着(empty)的空(position)。
将 \(n\) 个数按顺序插入 \(k\) 个空中等价于求方程 \(x_1+x_2+\cdots+x_k=n\) 的非负整数解数量。我们有组合数学的经典结论,解数为
于是有
- \(u\to v\)(\(u\lt v\))
条件就是 \(p+q\gt k\) 啦。
然后剩下的都是一样的。
时间复杂度 \(\Theta(n^4)\),可以拿到 \(40\) 分。
考虑优化。将只与 \(p\) 有关的提出来:
对于第一式:
对于第二式:
然后维护一下 \(f[v]\) 的前缀和就做完了。时间复杂度 \(\Theta(n^2)\)。
[ABC340G] Leaf Color
给出一棵节点数为 \(N\) 的树 \(T=(V,E)\),节点有颜色,节点 \(i\) 的颜色为 \(A_i\)。
求满足以下条件的 \(V\) 的子集 \(S\) 的数量:
- \(S\) 的导出子图 \(G\) 是一棵树;
- \(G\) 的叶节点颜色一致。
答案对 \(998244353\) 取模。
\(n\leq 2\times 10^5\)。
本题主要是利用虚树优化,这里记录一下 dp 的式子。
P6326 Shopping
P3780 [SDOI2017] 苹果树
树形 dp + 贪心
P3523 [POI2011] Dynamite
给定一棵树 \(T=(V,E)\),有一个关键节点集合 \(S\subseteq V\)。试选定 \(m\) 个点 \(u_1,u_2,\cdots,u_m\),最小化
你只需要求出这个最小化的值。
\(n\le 3\times 10^5\)。
看到最大值最小,想到二分。记二分到的值为 \(k\),问题转化为
最小化选定的点的数量 \(cnt\),使得 $$\max_{i=1}^{cnt} \min_{v\in S} \operatorname{dis}(u_i,v)\le k$$
以下称一个点被覆盖,当且仅当它离最近的关键点距离不大于 \(k\)。
考虑如何构造出一组解。一个比较显然的想法就是记录最远的未被覆盖的关键点到根的距离,然后当且仅当不得不选择根的时候,才选择根。这样构造出的方案是正确的,但是不是最优的。
不是最优的原因是,这样会使得一些点被重复覆盖。在最优方案中,整棵子树可能是被子树内的某个节点(而不是根节点覆盖)的。因此我们要尽可能减少重复覆盖的点的数量。
由于可能存在兄弟相互覆盖的情况,我们需要记录以下信息:
- 根节点到最远的未被覆盖的关键点的距离。记为 \(f[u]\)。
- 根节点到最近的被选择节点的距离。记为 \(g[u]\)。
转移是简单的。
初始时,\(f[u]=-\infty\),\(g[u]=+\infty\)。
当 \(g[u]+f[u]\le k\) 的时候,我们发现凭借 \(u\) 下最近的被选择的点,已经可以覆盖整棵子树了。所以此时我们令 \(f[u]= -\infty\)。
当 \(f[u]=k\) 的时候,必须选择 \(u\),否则就不合法了。所以此时我们令 \(f[u]=-\infty\),\(g[u]=0\)。并且令 \(cnt\gets cnt+1\)。
当 \(u\in S\)(\(u\) 是关键节点)且 \(g[u]\gt k\) 的时候,发现 \(u\) 下最近的被选择的点无法覆盖 \(u\)。此时我们可以选择让父亲覆盖 \(u\)。我们讨论:
- 如果 \(u\) 下没有未被覆盖的关键点(\(f[u]=-\infty\)),则 \(f[u]=0\)。
- 否则 \(f[u]\) 保持原样。
所以我们令 \(f[u]=\max(f[u],0)\)。
需要注意的是,如果根节点的 \(f[u]\ge 0\),我们还需要额外选择根节点。(想一想,为什么?)
综上,我们在 \(\Theta(n\log n)\) 的时间复杂度内解决了本题。
P3942 将军令
双倍经验。
“如果不是省选,大家大概不会这么轻易地分道扬镳吧?”
P2279 [HNOI2003] 消防局的设立 / P2016 战略游戏
四倍经验。
不过由于 \(k\) 的范围较小,也有不贪心直接 dp 的做法。

浙公网安备 33010602011771号