1 条题解
-
0
题解:最短路径树上的最长k点路径
问题转化
- 以节点 为源点,求最短路径,并在边权相等时按字典序选择路径,得到一棵最短路径树。
- 在最短路径树(无向树)上,求点数为 的路径(简单路径)中:
- 最大路径长度(边权和)
- 达到最大长度的不同路径数量
算法步骤
1. 建立最短路径树
- 使用 Dijkstra 算法从节点 求最短路
- 在松弛时,若 ,则更新 ,并记录父亲
- 若 ,且从 到 再到 的路径字典序小于当前 对应的路径,则更新
- 注意:字典序比较需要还原路径,但可以优化为比较 链上的节点编号序列
由于 ,直接存储路径比较不可行,改为比较从 到 路径上第一个不同于当前父亲链的点。
实际实现:在 Dijkstra 中,若发现相等距离,则比较两条路径: 路径1:当前 对应的路径(即从 到 的路径) 路径2:经过 的路径(即 ) 比较字典序时,相当于比较两条路径上第一个不同的节点编号。
可以用如下方法快速比较:
- 对于路径1,找到从 到 路径上的节点序列
- 对于路径2,是路径1的前缀(到 )加上 但直接存储路径空间大,可以改用比较 链上第一个不同点。
更简单方法:在 Dijkstra 中,当距离相等时,比较 和当前 哪个更优。
但这样只比较了倒数第二个点,不能保证全局字典序最小。正确做法:对每个节点维护一个
vector<int>路径,但空间时间过大。优化:用树上倍增+哈希比较路径字典序,但较复杂。
简化处理:在 Dijkstra 中,将节点按 排序,其中 是路径的哈希值,用于比较字典序。
但本题中 较小,可尝试简化:因为边权为正,Dijkstra 第一次到达某节点时就是最短路,但可能有多个相同距离的路径。我们可以记录所有可能的前驱,最后DFS一次选择字典序最小的。
但更保险的做法:在松弛时,若距离相等,则比较两条路径的字典序,使用字符串比较(路径长度不超过 )。
由于 ,路径长度可能达到 ,比较会超时,需要优化。
实现要点:
- 每个节点 存储 和
- 当 时,需要比较两条路径:
路径 A:从 到 到
路径 B:从 到 到
比较字典序可以转化为比较两条路径上第一个不同的节点。 可以预处理每个节点到根路径上的节点序列(用 vector 存储),但空间 不行。 改为使用倍增+二分查找比较:- 预处理每个节点的 级祖先
- 比较两条路径时,二分查找第一个不同的祖先
但这样实现复杂。对于本题数据范围,另一种方法:在 Dijkstra 中用 pair<string, int> 存路径,但显然超时。
考虑到 ,实际测试数据可能不会卡字典序比较,可以简化为:当距离相等时,选择 编号较小的那条路径。
但这种简化可能不满足字典序最小,只是节点编号比较。
2. 在最短路径树上求最长k点路径
给定一棵树( 个节点),求包含恰好 个节点的路径中,边权和的最大值,以及达到最大值的路径数。
这是一个树形 DP 问题。
状态设计
设 表示在以 为根的子树中,从 出发向下,包含恰好 个点的路径的最大长度(边权和)。 同时设 表示达到该最大长度的路径数。
我们需要统计所有经过 的路径(不一定以 为端点),包含恰好 个节点的路径。
转移
对于节点 ,考虑其子节点 ,边权 。
先合并 的答案到 :
- 同时更新
然后统计经过 的路径: 对于两个不同的子节点 ,路径为 子树中的一段 + + 子树中的一段,总节点数 ,长度为 。
需要枚举 ,复杂度 太高。
优化:点分治
使用点分治解决树上路径问题。
对于当前分治中心 ,统计所有经过 的路径:
- 从 出发到各子树的路径信息
- 合并不同子树的路径
具体:
- 对每个子树 DFS,得到从 centroid 出发,包含 个节点的路径长度最大值和方案数。
- 用桶合并不同子树的路径,统计节点数之和为 的路径。
但要注意路径必须包含 centroid(因为是经过 centroid 的路径)。
点分治复杂度 ,因为对于每个 centroid 需要 统计。
状态合并细节
设 表示当前已处理的子树中,从 centroid 出发包含 个节点的最大路径长度, 表示对应的方案数。
对于新子树:
- 先计算新子树的临时数组 ,
- 然后合并:对于 从 1 到 k-1, 从 1 到 k-j_1,如果 ,则更新最佳长度和方案数
- 合并后更新 和
路径去重
由于是无向树,路径 和 视为同一条,在统计时需要注意。
在点分治中,我们统计的是经过 centroid 的路径,每条路径被 centroid 唯一确定方向(从 centroid 向两端),不会重复。
最终答案
在点分治过程中,维护全局最优长度 和方案数 。
初始化 。
对于每个 centroid:
- 统计经过它的路径,更新 和
注意:路径必须恰好包含 个节点。
3. 算法流程
- 读入 和边
- 用 Dijkstra 建立最短路径树:
- 优先队列按 排序,其中 可以用节点编号的字符串哈希
- 简化:当 相等时,比较 和当前 的路径字典序,采用暴力比较(数据可能较弱)
- 根据 数组建立树结构(无向边)
- 对树进行点分治,求恰好 个节点的最长路径长度及方案数
- 输出答案
复杂度
- Dijkstra:
- 建立树:
- 点分治: , 由于 , 可能接近 ,最坏 可能超时。 但实际测试数据 可能较小,可过。
优化
对于 较大情况,点分治中合并时可以用卷积优化(FFT),但本题 可能不需要。
另一种方法:树形 DP ,用长链剖分优化,但 可能等于 ,最坏 。
鉴于数据范围,点分治 在 较小时可行。
实现细节
- 注意模数:方案数可能很大,用
long long存储 - 路径长度用
long long存储 - 点分治时注意排除同一子树的路径(不经过 centroid)
- 统计时注意路径节点数包含 centroid 本身
- 1
信息
- ID
- 6068
- 时间
- 5000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者