1 条题解
-
0
题目重述
我们有一棵 个结点的树。
每次操作:选择 和 ,把 到 的唯一路径上的所有点(除了 )直接连接为 的孩子(即删除原路径上的边,添加 到这些点的边)。
问:最少需要多少次操作,才能使得树的直径最小?
关键观察
-
时
直径已经是 ,不需要操作,答案为 。 -
时
可以证明最小的直径只能是 。
为什么?
因为我们可以任选一个根 ,然后反复操作使其它所有结点都直接成为 的孩子(即深度为 ),这样任意两个叶子之间的距离最多为 (经过 )。
所以最小直径可达 。因此,问题转化为:通过最少操作,使所有结点深度 ≤ 1(相对于某根 )。
操作与深度的关系
固定根 ,定义:
- : 在树中的深度(,孩子深度 父深度 )。
- 初始树中,深度 的结点需要被“拉”到深度 。
在一次操作中:
- 选择路径 ,删除路径上的边,再把 及其中间结点连到 。
- 注意:一次操作只能让路径末端的一个叶子(以及中间结点)提升到离 更近的位置。
- 实际上,一次操作最多可以将一条从 出发到某个深度 的叶子的路径上的所有点变为 的直接孩子。
因此:
- 对于深度 的叶子,每个这样的叶子至少需要一次操作(才能让它成为 的孩子)。
- 一次操作可以处理一条从 出发的路径上多个深度 >1 的点,但只能消除一个深度 >1 的叶子(因为路径末端必须是叶子)。
结论:
若固定根 ,最少操作数 = 深度 的叶子数量。
优化根的选择
因为我们可以自由选择 ,所以要最小化:
$$\text{操作数} = \min_{r} \left( \text{深度} > 1 \text{ 的叶子数} \right) $$
等价转换
深度 的叶子 = 叶子总数 − 深度 的叶子。
而深度 的叶子就是根 的邻居中为叶子的那些结点。
设 = 整棵树叶子总数。
设 = 根 的邻居中叶子的数量。那么对于根 ,操作数 = 。
所以:
其中 = 与 相邻的叶子结点数。
最终解法
- 读入树,计算每个结点的度数 。
- 叶子结点定义为 。
- 计算 = 叶子总数。
- 对每个结点 ,计算 = 它的邻居中是叶子的数量。
- 取 。
- 答案 = 。
- 特判 时答案为 (因为此时直径已经 ≤ 2)。
示例验证
以第一个样例(,边:1-2, 1-3, 2-4)为例:
- 叶子:3, 4 →
- 结点 1:邻居 2(度2), 3(度1) →
- 结点 2:邻居 1(度2), 4(度1) →
- 结点 3:邻居 1(度2) →
- 结点 4:邻居 2(度2) →
- 答案 =
符合题目输出。
复杂度
- 每个结点遍历邻居一次。
- 总复杂度 。
- 满足所有测试用例 总和 ≤ 。
对应 Python 标程思路
c1 = sum(deg[u] == 1 for u in range(n)) mx = max(sum(deg[v] == 1 for v in g[u]) for u in range(n)) return c1 - mx if n > 3 else 0完全符合上述推导。
-
- 1
信息
- ID
- 7044
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 5
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者