1 条题解
-
0
题目理解
我们有一棵 个节点的树,每个节点有一个颜色 (表示特产)。对于每个节点 ,定义其"独特城市"为那些与 的距离在整棵树中唯一的节点 (即不存在其他节点 与 的距离等于 )。
我们需要对每个节点 ,计算其所有独特城市所产特产的种类数。
关键观察
1. 独特城市的性质
对于树上的一个节点 ,其独特城市具有重要性质:
- 独特城市一定是 的最远点之一,或者在某些深度上只有一个节点
- 更准确地说,对于每个深度 ,如果深度 只有一个节点,那么这个节点就是独特的
- 如果某个深度有多个节点,那么这些节点都不是独特的
2. 树的直径与独特城市
重要定理:对于树中的任意节点 ,其独特城市一定位于树的某条直径的端点的路径上。
具体来说:
- 设树的直径端点为 和
- 对于任意节点 ,其独特城市要么在 到 的路径上,要么在 到 的路径上
3. 深度分析
考虑以节点 为根,设 表示 在根为 的树中的深度。
节点 是 的独特城市当且仅当:
- 在深度 上只有一个节点
换句话说,独特城市就是那些在深度分布中"独享"某个深度的节点。
算法设计
1. 整体思路
基于以上观察,我们可以设计以下算法:
- 找到树的直径端点 和
- 分别以 和 为根,预处理整棵树
- 对于每个节点 ,结合两个根下的信息计算答案
2. 具体步骤
步骤1:求直径
- 任选一个节点,通过 BFS/DFS 找到最远点
- 从 出发,通过 BFS/DFS 找到最远点
- 和 就是直径的两个端点
步骤2:预处理
以 为根:
- 计算每个节点的深度
- 记录每个深度的节点集合
- 记录每个节点的父节点信息
以 为根:
- 同样计算 等信息
步骤3:计算答案
对于每个节点 ,其独特城市主要出现在两个方向:
- 朝向 的方向
- 朝向 的方向
我们需要统计这两个方向上,哪些深度是唯一的,然后计算这些深度上的节点的特产种类数。
3. 高效实现方法
方法1:深度计数
对于每个根 ,我们维护:
- :深度 中颜色 出现的次数
- :深度 中独特的颜色集合
当我们在树上移动时(如从父节点到子节点),深度分布会发生变化,需要动态维护这些信息。
方法2:重链维护
利用树链剖分的思想:
- 对树进行重链剖分
- 在每条重链上维护深度信息
- 通过重链快速更新和查询
4. 算法优化
关键优化:我们不需要对每个节点都重新计算所有信息,而是可以利用树的遍历顺序,在 DFS 过程中动态维护:
- 进入一个节点时,添加该节点对深度分布的影响
- 离开一个节点时,撤销该节点的影响
- 这样可以在 或 时间内更新状态
复杂度分析
- 求直径:
- 两次DFS预处理:
- 动态维护深度信息:使用合适的数据结构,每次更新
- 总复杂度:,对于 可以接受
特殊情况处理
Subtask 2:
所有城市产同一种特产,答案只能是 或 ,大大简化。
Subtask 3: 且
每个城市的特产都不同,问题简化为统计独特城市的数量。
实现细节
1. 数据结构选择
- 使用
std::set或std::unordered_set维护每个深度的节点颜色 - 使用数组记录每个深度的节点数量
- 使用栈或递归维护DFS状态
2. 信息维护
在DFS过程中:
- 进入节点 时:
- 将 的颜色加入到当前深度的集合中
- 更新深度计数
- 处理子树
- 离开节点 时:
- 从当前深度的集合中移除 的颜色
- 恢复深度计数
3. 答案计算
对于每个节点 ,其独特城市对应的颜色集合就是:
- 所有深度 满足"该深度只有一个节点"的节点的颜色集合
- 取这些颜色的并集
总结
这道题的核心在于深入理解树的结构性质,特别是直径和深度分布在确定独特城市时的作用:
- 结构分析:利用树的直径性质缩小候选范围
- 深度唯一性:独特城市对应于深度分布中的唯一节点
- 动态维护:通过DFS遍历动态更新深度信息
- 高效计数:使用合适的数据结构快速计算颜色种类数
这种"结构性质 + 动态维护"的思路在解决树上的复杂计数问题时非常有效,是算法竞赛中的高级技巧。
- 1
信息
- ID
- 5242
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者