#L3951. 「USACO 2023.2 Platinum」Watching Cowflix
「USACO 2023.2 Platinum」Watching Cowflix
题意理解
我们有一棵 个节点的树,每个节点有一个标记 , 表示 Bessie 在这个节点看视频, 表示不看。
我们需要选择若干互不相交的连通块 ,使得每个 的节点都被某个连通块包含。
的节点可以不在任何连通块中。
一个连通块 的代价是 ,总代价为 。
我们要对 分别求最小总代价。
初步分析
设连通块数为 ,所有连通块节点数总和为 ,那么总代价为: [ S + k \cdot C ]
是所有连通块的大小之和, 是连通块数。
等价形式
我们要求: [ \min_{覆盖} \left( \sum_{i} |c_i| + k \cdot C \right) ]
这等价于: [ \min_{覆盖} \left( \sum_{i} (|c_i| + k) \right) ]
动态规划思路
考虑在树上做 DP。
设 表示节点 所在的连通块向上不延伸(即 所在连通块在子树内封闭)的最小总代价(只考虑 的子树)。
表示节点 所在的连通块向上延伸(即 所在连通块可能和父节点连通)的最小总代价。
状态转移
情况 1: 是标记为 的节点
- 如果 不在任何连通块中(即 不被选入连通块),那么 的每个子节点 必须自己封闭,即取 。
- 如果 在某个连通块中(为了向上延伸),那么 必须和至少一个子节点连通,其他子节点可以封闭或也连通。
但注意: 的节点可以不在任何连通块,所以 可以是不包含 的情况。
实际上更简单的定义是:
设:
- : 子树内, 不在任何连通块的最小代价
- : 子树内, 在某个连通块的最小代价
重新定义
更常见的做法是:
: 不在任何连通块中(那么所有 的子孙必须被覆盖,但 不参与连通块)
: 在某个连通块中,且这个连通块在子树内已经封闭(即不与父节点连通)
: 在某个连通块中,且这个连通块准备与父节点连通
但这样状态有点多。我们参考类似题目的经典做法:
经典 DP 状态
设:
- : 不在任何连通块,子树内所有 节点已被覆盖的最小代价
- : 在某个连通块中,且该连通块尚未封闭(可以向上延伸)的最小代价
- : 在某个连通块中,且该连通块已封闭的最小代价
转移:
-
: 不在连通块
那么所有子节点 必须已封闭(状态 2),因为不能向上延伸到 。
所以: [ dp[u][0] = \sum_v dp[v][2] ] 但注意:如果 ,那么 必须被覆盖,所以 不可能(设为无穷大)。 -
: 在连通块且向上延伸
那么 的连通块至少包含 自己,代价目前为 (节点数), 的代价先不算,最后统一算。
子节点 可以选择:- 与 连通(状态 1),这样节点数合并,不增加 的代价
- 不连通(状态 2),这样增加一个连通块,代价增加
我们要最小化 节点数 + 连通块数。
设 是与 连通的子节点数,那么: [ dp[u][1] = 1 + \sum_v \min(dp[v][1], dp[v][2] + k) ] 因为与 连通的子节点用 (不增加 ,节点数合并),不连通的用 (增加 代价)。 -
: 在连通块且封闭
有两种可能:- 单独一个连通块:代价 ,子节点全部取
- 与至少一个子节点连通,然后封闭:那么选一个子节点 用 (与 连通),其他子节点用 ?不对,这样复杂。
实际上更简单:$dp[u][2] = \min(1 + k + \sum_v dp[v][2],\; dp[u][1])$
但 可能比 小吗?不一定,因为 没算封闭的 代价。正确做法:
- 要么 单独成块:
- 要么 与子节点连通并在某个子树封闭:这其实包含在 的某个分支里?不, 是向上开放的。
所以 $dp[u][2] = \min(1 + k + \sum_v dp[v][2],\; \min\limits_v [dp[v][2] - \min(dp[v][1], dp[v][2]+k) + dp[u][1]])$?太复杂。
更简洁的状态(常见解法)
设:
- : 不在连通块,子树内所有 被覆盖
- : 在连通块且连通块可能向上延伸
- 最终封闭的块在根节点处理
转移:
-
:
如果 ,不可能(inf)。
否则 ?
不对,如果 不在块,子节点可以单独成块()或不在块(),但必须覆盖所有 。其实 ?
因为 不在块,每个子节点必须自己成块( 表示 在块且开放,但 必须封闭,所以应该是 ?)
实际上 已经包含 所在块的节点数,但没加 ,所以加 表示 单独成块。所以: [ dp[u][0] = \sum_v (dp[v][1] + k) ] 但这样可能多算 ?因为每个子节点都成块。
-
:
在块,节点数至少 1。
子节点可以选择:- 与 连通:用 (不加 )
- 不与 连通:用 (单独成块)或 (不在块,但必须 才能不在块)
所以: [ dp[u][1] = 1 + \sum_v \min(dp[v][1],; dp[v][1]+k,; dp[v][0]) ] 但 ,所以去掉第二个。
所以: [ dp[u][1] = 1 + \sum_v \min(dp[v][1], dp[v][0]) ] 注意 只在 时有限,否则无穷大。
初始化
叶子节点:
- 如果 :, (自己成块)
- 如果 :, (可以成块,也可以不成块,但 是成块的情况)
最终答案
根节点(任意根)的答案: [ ans = \min(dp[root][0], dp[root][1] + k) ] 因为如果根在块且开放,则必须封闭(加 )。
对 的处理
我们要对 都求答案。
观察转移中 只出现在 的 和最后的 中。
实际上 $dp[u][0] = \sum (dp[v][1]+k) = \sum dp[v][1] + deg(u) \cdot k$。
所以 是关于 的线性函数,可以证明 和 都是关于 的分段线性凸函数,段数是 。
因此可以用凸包优化或直接枚举 做 次 DP,总复杂度 对于 太大。
优化
注意到 (连通块数)不会超过 的个数,并且随着 增大, 单调不增。
因此可以按 分类,对每个可能的 求出最小的 ,然后总代价 取 。
这个思路是:
对于固定的 ,我们想要最小化 (覆盖所有 的 个连通块的总大小)。
那么问题变成:选 个连通块覆盖所有 ,最小化总节点数。
这个可以用贪心/树形 DP 求出 。
然后 。
关于 是凸的?可以证明是的。
因此可以斜率优化, 求出所有 的答案。
最终算法(思路)
- 求出 表示用 个连通块覆盖所有 的最小总大小。
- 。
- 输出 。
求 可以用树形 DP 合并凸包的方法(难度较大),但本题 ,需要 或 做法。
算法标签
树形DP
凸优化
斜率优化
连通块覆盖