洛谷上好多 O2 过了的假算法啊 → →
# 题面
> Linked 洛谷 P3117
点击展开/折叠题面
有一棵点数为 $n$ 的树,树边有边权。给你一个在 $0 \sim n$ 之内的正整数 $k$,你要在这棵树中选择 $k$ 个点,将其染成黑色,并将其他的 $n-k$ 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。
# 解析
先从暴力开始想,即 $f_{u,i}$ 表示 $u$ 的子树中,有 $i$ 个白点,此时已经确定的受益最大值。
(我一开始还想错了 QwQ)若 $u$ 的子树内有 $i$ 个白点,那 $u$ 的子树外有 $k-i$ 个白点,于是 $u$ 到 $u$ 的父亲的边对于白点来说提供了 $(k-i)i$ 次贡献,对于黑点同理。则 $u$ 到父亲的边 $e$ 总贡献为 $C(u,i)=val_e[(k-i)i+(siz_u-i)(n-k-siz_u+i)]$。
这就是一个背包问题,于是就有下面的递推式:
树形背包过程中涉及到两个背包(即“$u$ 与 $v$ 之前子节点的背包”和“子节点 $v$ 的背包”)的合并,如果直接合并,因为第二维是 $O(n)$ 的,理论复杂度合并一次是 $O(n^2)$,总复杂度 $O(n^3)$。但是跑不满,可以利用 $v$ 子树的背包第二维最大只有 $siz_v$ 优化一下,卡一卡还是能过。
我想到的是一种 $O(n^2\log n)$ 的算法,利用了树上启发式合并的思想。
考虑合并背包的过程:最初 $u$ 的背包只是 $u$ 单点,大小为 $1$;然后和 $u$ 的子节点依次合并。
所以我就想,$u$ 先和它的重儿子合并,然后再和它的轻儿子合并,就可以做到 $O(n^2\log n)$。
为什么?分开考虑。
考虑 $u$ 和它的重儿子 $v$ 合并,因为是第一次合并,$u$ 的背包大小为 $1$,$v$ 的背包大小为 $siz_v$,粗略看为 $O(n)$,这样两个背包合并是 $O(n)$ 的。于是对于每个 $u$ 都会有这样一次合并,于是复杂度是 $O(n^2)$。
考虑 $u$ 和它的轻儿子 $v$ 合并,此时 $u$ 已经和一些子节点合并过了,背包大小为 $O(n)$;$v$ 的背包大小为 $siz_v$。由树上启发式合并可得,记 $lson_u$ 是 $u$ 的轻儿子点集:
而合并 $v$ 与 $u$ 的背包的复杂度粗略记作 $O(n\times siz_v)$(因为其实 $u$ 的背包大小比 $n$ 小),则有:
因此总复杂度为 $O(n^2\log n)$。
# 源代码
1 | /*Lucky_Glass*/ |
THE END
Thanks for reading!
> Linked 那一天从梦中醒来-网易云