1 条题解
-
0
题意概述
给定一个无向图,每条边有:
- 长度 (影响通行时间)
- 海拔 (影响洪水期的通行)
在水位线 时,只能走海拔严格大于 的边。
多组询问:从点 出发,在水位 下,开车到 号点的最短步行距离(开车只能走海拔 的边,下车后步行走任意边)。
算法框架
1. 最短路预处理(Dijkstra)
inline void dijkstra() { // 计算从1号点到所有点的最短距离 // 因为最终要回到1号点,所以反向思考:从1出发到各点的距离 }- 计算从 1 号点 到所有点的最短路径长度
dis[i] - 这样对于任意点 ,
dis[u]就是从 步行到 1 号点的最短距离
2. Kruskal 重构树(按海拔构建最大生成树)
inline void kruskal() { sort(e + 1, e + 1 + m, cmp); // 按海拔降序排序 // 构建重构树:新节点 tot 代表一条边,左右儿子是连通分量 }- 按海拔从大到小排序边(最大生成树)
- 构建 Kruskal 重构树:
- 新节点
tot代表一条边,权值为该边海拔 - 左右儿子
ls[tot],rs[tot]代表两个连通分量
- 新节点
- 重构树性质:节点到根路径上的海拔权值递减
3. DFS 预处理重构树
void dfs(int u) { // 预处理倍增数组 f[u][i] // 计算 cost[u]:以 u 为根的子树中,所有原始节点到 1 号点的最小 dis 值 }- 预处理倍增数组
f[u][i]用于快速跳祖先 cost[u]表示:在重构树中,以 为根的子树内,所有原始节点到 1 号点的最小dis值- 即:如果开车能到达这个子树,那么步行的最小距离就是
cost[u]
- 即:如果开车能到达这个子树,那么步行的最小距离就是
4. 在线查询处理
for (int i = 20; i >= 0; i--) if (val[f[u][i]] > p) u = f[u][i]; cout << cost[u] << endl;查询 :
- 在重构树上从 向上跳,找到海拔 > p 的最近祖先
- 这个祖先的子树代表:从 开车(经过海拔 > p 的边)能到达的所有点
- 答案就是这个子树中所有点的最小
dis值(即cost[ancestor])
关键技巧
1. 重构树的性质利用
- 重构树中,节点到根路径的边权递减(因为按海拔降序构建)
- 对于水位 ,从 能开车到达的区域正好是: 的某个祖先的整个子树
2. cost 数组的意义
cost[u] = min(dis[u], min(cost[ls[u]], cost[rs[u]]))- 叶子节点(原始节点):
cost[u] = dis[u] - 内部节点(重构节点):子树中的最小
dis值
3. 倍增优化
- 预处理
f[u][i]实现 的祖先跳跃 - 快速找到满足
val[ancestor] > p的最高祖先
时间复杂度
- Dijkstra:
- Kruskal:
- DFS 预处理:
- 每次查询:
- 总体:
总结
这个解法巧妙结合了:
- 最短路:计算步行距离
- Kruskal 重构树:处理海拔限制下的连通性
- 树上倍增:快速定位满足条件的祖先
- 子树最小值:预处理最优答案
是一个典型的 图论 + 数据结构 综合题,考察对多种算法的融合运用能力。
- 1
信息
- ID
- 4401
- 时间
- 3000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 31
- 已通过
- 1
- 上传者