尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

10.16 闲话-k 短路

10.16 闲话-k 短路
📅 发布时间:2026/6/20 0:17:20
发晚了qwq

10.16 闲话-k 短路

Part.1 左偏树

左偏树是一个堆,支持 \(O(\log n)\) 合并。

假设 \(dist_i\) 表示离 \(i\) 最近的儿子数量不为 \(2\) 的儿子的距离 + 1,孤立点设为 0。

容易发现 \(dist_i \le \log n\) 。

证明考虑如果有 \(dist_i\) 则树高一定大于了 \(2^{dist_i-1}\) 。

那么如果钦定每一个节点的右儿子 \(dist\) 小于左儿子 \(dist\) ,那么这棵树就是左偏树。

不难发现此时 \(dist_i=dis_{rs_i}+1\) 。

故左偏树任意节点右链是 \(O(\log n)\) 的!

此时就很暴力了,合并时考虑将两个树树根较小的作为新的跟,然后将另一个跟和右儿子合并作为新的右儿子,注意此时可能破坏左偏结构,需要判断是否要交换左右儿子。

发现复杂度是 \(O(\log n)\) 。

int Merge(int x, int y)
{if (!x || !y) return x | y;if (val[x] > val[y]) swap(x, y);rs[x] = Merge(rs[x], y);fa[rs[x]] = x;if (dis[rs[x]] > dis[ls[x]]) swap(ls[x], rs[x]);dis[x] = dis[rs[x]] + 1;return x;
}

删除也很暴力,直接合并左右儿子,然后自底向上更新 \(dis\),由于可能发生更新的都是右儿子,而右链长度是 \(O(\log n)\) 的,所以复杂度是 \(O(\log n)\) 的。

void PushUp(int x)
{if (!x) return ;if (dis[x] != dis[rs[x]] + 1){dis[x] = dis[rs[x]] + 1;PushUp(fa[x]);}
}void Delete(int x)
{int y = Merge(ls[x], rs[x]);fa[y] = fa[x];if (ls[fa[x]] == x) ls[fa[x]] = y;if (rs[fa[x]] == x) rs[fa[x]] = y;if (dis[rs[fa[x]]] > dis[ls[fa[x]]]) swap(ls[fa[x]], rs[fa[x]]);PushUp(fa[y]);
}

然后其实就没了,会了这些,加上一些高端的并查集应用,就可以过穿可并堆的题目了。

Part.2 可持久化可并堆

发现上述过程完美符合可持久化操作,只需要 Merge 时新建一个节点即可。


int Merge(int x, int y)
{if (!x || !y) return x | y;if (val[x] > val[y]) swap(x, y);int p = ++cnt;ls[p] = ls[x], val[p] = val[x];rs[p] = Merge(rs[x], y);fa[rs[p]] = p;if (dis[rs[p]] > dis[ls[p]]) swap(ls[p], rs[p]);dis[p] = dis[rs[p]] + 1;return p;
}

Part.3 k 短路

会了一些前置知识后就可以开始学习 k 短路了。

其实我们可以发现一件事情就是一条更劣的路一定是经过了一些最短路的边,然后经过一些不在最短路上的边。

那么可以先建出到终点的最短路树(假设每个点到终点的距离为 \(h_i\)),然后将不在树边上的边成为偏离边。

这时发现经过一个偏离边(假设边权为 \(w\))的增加的代价其实就是 \(w+h_{to}-h_{from}\)

由于这是有向图,那么可以得知,对于一个路径,相邻的非树边中上一条的终点一定是这条边起点的儿孙。

那么便可以将一条边终点不变的添加到起点的子树中,将问题转化为统计从 \(s\) 到任意节点的 \(k\) 短路。

这时又注意到,对于每个要去转移的节点,如果其边越小转移出去越优,那么每次只更新最小的边,然后将这条边删去,发现这是一个堆能干的(每次拿出一个堆,弹掉栈顶)。

此时就发现,原本放到子树中的那一步操作竟然可以可持久化可并堆。

这样就愉快的通过了 k 短路。

复杂度 \(O((n+m)\log m+k\log k)\)

code
#include <iostream>
#include <cassert>
#include <vector>
#include <cstring>
#include <queue>
#include <climits>using namespace std;const int N = 2e6 + 10, M = 6000;#define fi first
#define se second
#define emp emplace_backusing ld = double;
using pii = pair <int, int>;
const ld inf = 1e300, eps = 1e-9;int cnt, ls[N], rs[N], rt[M], to[N], d[N], pre[M];
ld val[N];int Merge(int x, int y)
{if (!x || !y) return x | y;if (val[x] > val[y]) swap(x, y);int p = ++cnt;ls[p] = ls[x], val[p] = val[x], to[p] = to[x];rs[p] = Merge(rs[x], y);if (d[ls[p]] < d[rs[p]]) swap(ls[p], rs[p]);d[p] = d[rs[p]] + 1;return p;
}int New(ld v, int _to) {++cnt; val[cnt] = v, to[cnt] = _to; return cnt;}void Insert(int &x, ld v, int to)
{x = Merge(x, New(v, to));
}int n, m;
ld dis[M], pw[M];
vector <pair <int, ld>> E[M], T[M], NT[M];void dfs(int x, int fa)
{for (auto [to, w] : NT[x]) Insert(rt[x], w, to);rt[x] = Merge(rt[x], rt[fa]);for (auto [to, w] : T[x]){if (to == fa) continue;dfs(to, x);}
}int Solve(int s, ld k)
{priority_queue <pair <ld, int>> q;q.push({-(dis[s] + val[rt[s]]), rt[s]});int timer = 0;++timer, k -= dis[s];if (k < 0) return 0;while (q.size()){int x = q.top().se;ld y = -q.top().fi; q.pop();if (!x) continue;k -= y, ++timer;if (k < 0) return timer - 1;q.push({-(y + val[rt[to[x]]]), rt[to[x]]});q.push({-(y - val[x] + val[ls[x]]), ls[x]});q.push({-(y - val[x] + val[rs[x]]), rs[x]});}return timer;
}bool vis[M];
void dij(int s)
{for (int i = 1; i <= n; i++) dis[i] = inf;priority_queue <pair <ld, int>> q;q.push({0, s});dis[s] = 0;while (q.size()){int x = q.top().se; q.pop();if (vis[x]) continue;vis[x] = 1;for (auto [to, w] : E[x]){if (dis[to] > dis[x] + w){pre[to] = x, pw[to] = w;dis[to] = dis[x] + w;q.push({-dis[to], to});}}}
}bool flag[M];signed main()
{// freopen("data.in", "r", stdin); freopen("data.out", "w", stdout);ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);ld k; cin >> n >> m >> k;for (int i = 1; i <= m; i++){int x, y; ld w; cin >> x >> y >> w;if (x == n) continue;E[y].emp(x, w);}dij(n);for (int i = 1; i <= n; i++){for (auto [to, w] : E[i]){if (pre[to] == i && abs(pw[to] - w) < eps && !flag[to]) flag[to] = 1, T[i].emp(to, w);else NT[to].emp(i, w + dis[i] - dis[to]);}}dfs(n, 0);cout << Solve(1, k) << '\n';return 0;
}

相关新闻

  • AI深度学习平台快速诊断肌张力障碍
  • 2025年干燥机厂家推荐排行榜,小型喷雾/实验室离心喷雾/双锥回转真空/搪瓷双锥/旋转闪蒸/振动流化床/真空耙式/单层带式/多层带式/立式沸腾/卧式沸腾/滚筒刮板干燥机!
  • 2025年CNC高压清洗机厂家推荐排行榜,CNC全自动高压清洗机,CNC去毛刺清洗机,工业CNC高压清洗机公司推荐!

最新新闻

  • MC68HC908低功耗模式与SPI通信:嵌入式系统节能与可靠通信设计
  • CANN/asc-devkit:asc_e2m1x22bfloat16函数
  • 2026年6月安徽VI设计实力企业选型指南:意赫创意的综合优势分析 - 品牌鉴赏官2026
  • Crypto++ 实战:5分钟构建企业级C++加密方案库
  • MySQL查询优化的5个核心技巧与工具:快速提升数据库性能的终极指南
  • FPGA_Webserver约束文件配置:Nexys Video开发板引脚分配与时序约束

日新闻

  • 信任的进化:技术实现详解——如何用JavaScript构建博弈论模拟器
  • Terrakube自定义工作流:如何集成OPA、Infracost等工具扩展IaC能力
  • grunt-concurrent快速入门:5分钟学会并行运行Grunt任务

周新闻

  • 3步解锁iOS设备:applera1n激活锁绕过完全指南
  • 39 2026 人工智能证书终极盘点,普通人选 AI 证书可以从这些方向入手
  • Redis 暴露公网有多危险?从端口检查到补救步骤

月新闻

  • 【总结】入门篇:50句话让你记住架构核心概念
  • WeChatMsg技术方案解析:实现Mac微信数据自主管理的完整解决方案
  • WeChatMsg:革新性微信数据备份方案,打造你的专属数字记忆库

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号