P2934 [USACO09JAN] Safe Travel G 题解

题意

给定一张 n n n 个点 m m m 条边的无向图,对于每个除 1 1 1 以外的点 u u u,求在不允许经过原来从 1 1 1 u u u 的最短路径的最后一条边时, 1 1 1 u u u 的最短路。

保证 1 1 1 到其他点的最短路唯一,无解输出 -1

解法

如果每次暴力删边后再跑一次最短路复杂度显然不行。题目中给了最短路唯一的条件,可以建出最短路径树,把每条从 1 1 1 到其他所有点的最短路径上的边拎出来单独建一棵树。

对于一条最短路径上的最后一条边 ( u , v ) (u,v) (u,v) u u u v v v 的祖先),我们删去它后会导致树上 1 1 1 v v v 不连通(最短路径唯一),意味着需要走至少一条非树边。

推理可知:新的最短路只会经过一条非树边。假设需要经过两条及以上的非树边,那么总是有至少一条非树边可以用几条树边拼起来(否则这条非树边就应当是树边)。可以脑补一下走多条非树边的情况,一定存在一个更优的只走一条非树边的路径。

所以,从 1 1 1 v v v 的新最短路径可以用一条非树边连接两条树上路径组成。不妨枚举每一条非树边对新最短路径的贡献。

对于一条非树边 ( u , v ) (u,v) (u,v),记 u , v u,v u,v 的最近公共祖先为 l c a lca lca,如果将其加进最短路树中,则会产生一个环,环上的点为 u → l c a u\to lca ulca v → l c a v\to lca vlca 两条链上的点。那么 ( u , v ) (u,v) (u,v) 可能可以更新的点即为这个环上除了 l c a lca lca 的其他点(对 u u u v v v 也能够产生贡献)。

  • 如果一个点 x x x 在这个环上,且不是 l c a lca lca,那么断开它和其父亲的这条边后,就可以以另一个方向绕这个环(比如说一开始要从 l c a lca lca u u u 的方向走,那么这次就往 v v v 的方向走,然后再走 ( u , v ) (u,v) (u,v) 这条边,从 u u u 往上爬爬到 x x x),途中只会经过 ( u , v ) (u,v) (u,v) 这一条非树边。
  • 如果一个点 x x x l c a lca lca 及其祖先,那么断开这么一条边后从 1 1 1 甚至无法到达这个环,再多花一条非树边走到环上不如另找一个非树边,走另一个环,回到第一种情况。
  • 如果一个点 x x x u u u v v v 的后代,那么断开这条边后也无法到达环上,与情况二相同,不如另找一个环。

此时,如果点 x x x 满足条件并打算走 ( u , v ) (u,v) (u,v) 这条边,设 1 → a 1\to a 1a 的最短路(即树上路径长度)为 d i s a dis_a disa,则新的 1 → x 1\to x 1x 的路径长度为 d i s u + w ( u , v ) + d i s v − d i s i dis_u+w(u,v)+dis_v-dis_i disu+w(u,v)+disvdisi,其中 w ( u , v ) w(u,v) w(u,v) ( u , v ) (u,v) (u,v) 的边权。建议看图理解。

  • u u u v v v​ 的位置反过来是同理的。

d i s i dis_i disi 是不变的,所以我们把每条边按照 d i s u + w ( u , v ) + d i s v dis_u+w(u,v)+dis_v disu+w(u,v)+disv 从小到大进行排序,每次遍历一遍环上没有被确定答案的点并更新答案,这样可以保证每个点被最优的非树边计算答案,不会重复计算。

实现

Dijkstra 算出最短路,根据松弛操作 d i s v ← min ⁡ ( d i s v , d i s u + w ( u , v ) ) dis_v\gets\min(dis_v,dis_u+w(u,v)) disvmin(disv,disu+w(u,v)),只需在算法结束后遍历每条边 ( u , v ) (u,v) (u,v),判断 d i s v dis_v disv 是否与 d i s u + w ( u , v ) dis_u+w(u,v) disu+w(u,v) 相等,如果是则说明有一条最短路经过了 ( u , v ) (u,v) (u,v) ( u , v ) (u,v) (u,v) 即为最短路树树边。

跳过已经确定答案的点考虑使用并查集缩点。在环上的点的答案被更新以后,我们把它们全部归到一个并查集里,根节点为 l c a lca lca 或者它的祖先。在遍历另一个环时,如果环上某一段被更新完了,则直接用并查集往上跳到环上下一个没被更新过的点。

复杂度不太会分析所以就是 O(能过) 因为每个点的答案只会被计算一次,否则会被跳过,复杂度应该就是 O ( m log ⁡ m ) O(m\log m) O(mlogm)​ 的(对非树边进行排序)。

个人认为这个并查集更像是记录每个点最近的没被计算过贡献的祖先,加快了在环上跳的过程;而路径压缩的过程就是把跳的好几下压缩成跳一下就到。

代码

// Dijkstra构建最短路径树
// 按照dis[u]+w[u][v]+dis[v]排序
// 最后并查集缩点(类似Kruskal)
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 2e5 + 5;
struct Edge { int u,v; ll w; int nxt;Edge(int a = 0,int b = 0,ll c = 0,int d = 0) { u = a, v = b, w = c, nxt = d; }
} e[maxn << 1];
int head[maxn],cnt,n;
void addEdge(int u,int v,ll w) {e[++ cnt] = Edge(u,v,w,head[u]);head[u] = cnt;
}
ll dis[maxn]; bool vis[maxn]; int tot;
pair<ll,pair<int,int> > newEdge[maxn << 1];
vector<int> mp[maxn];
void Dijkstra() {#define type pair<ll,int>priority_queue<type,vector<type>,greater<type> > q;for (int i = 2;i <= n;i ++) dis[i] = 2e18;q.push({0,1}); dis[1] = 0;while (!q.empty()) {int u = q.top().second; q.pop();if (vis[u]) continue; vis[u] = true;for (int i = head[u];i;i = e[i].nxt) {int v = e[i].v; ll w = e[i].w;if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;if (!vis[v]) q.push({dis[v], v});}}}// 选出非树边for (int i = 1;i <= cnt;i ++) {int u = e[i].u, v = e[i].v, w = e[i].w;if (dis[v] != dis[u] + w && u <= v)newEdge[++ tot] = {dis[u] + dis[v] + w, {u, v}};else if (dis[v] == dis[u] + w) mp[u].push_back(v);}#undef type
}
int dep[maxn],fat[maxn]; // 算深度和每个点的父亲。
void dfs(int u,int fa) {dep[u] = dep[fat[u] = fa] + 1;for (auto v : mp[u])if (v != fa) dfs(v,u);
}
int m,fa[maxn]; ll ans[maxn];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
int main() {scanf("%d%d",&n,&m); ll w;for (int i = 1,u,v;i <= m;i ++) {scanf("%d%d%lld",&u,&v,&w);addEdge(u,v,w); addEdge(v,u,w);}Dijkstra(); dfs(1,0);sort(newEdge + 1,newEdge + tot + 1);for (int i = 1;i <= n;i ++)ans[i] = -1, fa[i] = i;// 算答案for (int i = 1;i <= tot;i ++) {int u = find(newEdge[i].second.first); // 直接找到一个最近的没被计算过答案的点。int v = find(newEdge[i].second.second);ll w = newEdge[i].first;while (u != v) { // 当u,v相等时说明跳到了lca及以上,不在环上了。if (dep[u] < dep[v]) u ^= v, v ^= u, u ^= v; // 每次把u,v中深度较大的点往上挪。ans[u] = w - dis[u], fa[u] = fat[u]; // 可以往上跳但还在环上,那么自己被计算过了,下一个没被计算过的点就是fa[fat[u]]。u = find(u); // 往上跳,注意有路径压缩。}}for (int i = 2;i <= n;i ++)printf("%lld\n",ans[i]);return 0;
}
// 记得开long long!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/278422.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git撤回代码提交commit或者修改commit提交注释

执行commit后&#xff0c;还没执行push时&#xff0c;想要撤销之前的提交commit 撤销提交 使用命令&#xff1a; git reset --soft HEAD^命令详解&#xff1a; HEAD^ 表示上一个版本&#xff0c;即上一次的commit&#xff0c;也可以写成HEAD~1 如果进行两次的commit&#xf…

鸿蒙Socket通信示例(TCP通信)

前言 DevEco Studio版本&#xff1a;4.0.0.600 参考链接&#xff1a;OpenHarmony Socket 效果 TCPSocket 1、bind绑定本地IP地址 private bindTcpSocket() {let localAddress resolveIP(wifi.getIpInfo().ipAddress)console.info("111111111 localAddress: " …

配置阿里云加速器

国内镜像中心常用阿里云或者网易云。在本地docker中指定要使用国内加速器的地址后&#xff0c;就可以直接从阿里云镜像中心下载镜像。 2024阿里云-上云采购季-阿里云 根据操作系统来选择。

RequestResponse使用

文章目录 一、Request&Response介绍二、Request 继承体系三、Request 获取请求数据1、获取请求数据方法&#xff08;1&#xff09;、请求行&#xff08;2&#xff09;、请求头&#xff08;3&#xff09;、请求体 2、通过方式获取请求参数3、IDEA模板创建Servlet4、请求参数…

【AUTOSAR】【通信栈】Fls

AUTOSAR专栏——总目录-CSDN博客文章浏览阅读592次。本文主要汇总该专栏文章,以方便各位读者阅读。https://blog.csdn.net/qq_42357877/article/details/132072415?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22132072415%22…

金融知识分享系列之:MACD指标精讲

金融知识分享系列之&#xff1a;MACD指标精讲 一、MACD指标二、指标原理三、MACD指标参考用法四、MACD计算步骤五、MACD分析要素六、根据快线DIF位置判断趋势七、金叉死叉作为多空信号八、快线位置交叉信号九、指标背离判断行情反转十、差离值的正负十一、差离值的变化十二、指…

C语言之数据在计算机内部的存储

文章目录 一、前言二、类型的基本归类1、整型家族2、浮点数家族3、构造类型4、指针类型 三、整型在内存中的存储1、原码、反码、补码1.1 概念1.2 原码与补码的转换形式1.3 计算机内部的存储编码 2、大小端介绍~~2.1 为什么要有大端和小端之分&#xff1f;2.2 大&#xff08;小&…

https代理相对socks5代理有什么优势?

随着互联网的快速发展&#xff0c;代理服务已成为许多人在访问敏感或地理位置受限的网站时所依赖的工具。其中&#xff0c;HTTPS代理和SOCKS5代理是两种最常用的代理服务类型。本文将探讨HTTPS代理相对SOCKS5代理的优势。 1、安全性 HTTPS代理使用SSL/TLS协议对客户端和代理服…

力扣刷题Days20-151. 反转字符串中的单词(js)

目录 1,题目 2&#xff0c;代码 1&#xff0c;利用js函数 2&#xff0c;双指针 3&#xff0c;双指针加队列 3&#xff0c;学习与总结 1&#xff0c;正则表达式 / \s /&#xff1a; 2&#xff0c;结合使用 split 和正则表达式&#xff1a; 1,题目 给你一个字符串 s &am…

[HNCTF 2022 WEEK2]e@sy_flower

获取基本信息 获取关键字符串 进来“开门红” 上一篇博客才发现这个 按u转换为二进制 有个无效db&#xff0c;最简单的花指令 nop掉 重新u一下p一下 就正常了 然后编译完main函数 int __cdecl __noreturn main(int argc, const char **argv, const char **envp) {signed in…

二、python基础

一、关键字&#xff08;保留字&#xff09; 指在python中赋予特定意义的一类单词&#xff0c;不能将关键字作为函数、变量、类、模块的名称 import keyword#利用内存模块keyword print(keyword.kwlist)#输出所有关键 print(len(keyword.kwlist))#利用内置函数len()输出关键字的…

idea项目mapper.xml中的SQL语句黄色下划线去除

问题描述 当我们使用idea开发java项目时&#xff0c;经常会与数据库打交道&#xff0c;一般在使用mybatis的时候需要写一大堆的mapper.xml以及SQL语句&#xff0c;每当写完SQL语句的时候总是有黄色下划线&#xff0c;看着很不舒服。 解决方案&#xff1a; 修改idea的配置 Edi…

视觉问答(Visual_Question_Answering, VQA)介绍

1.背景 VQA&#xff08;Visual Question Answering&#xff09;指的是&#xff0c;给机器一张图片和一个开放式的的自然语言问题&#xff0c;要求机器输出自然语言答案。答案可以是以下任何形式&#xff1a;短语、单词、 (yes/no)、从几个可能的答案中选择正确答案。VQA是一个…

21-分支和循环语句_while语句(中)(初阶)

21-2 代码准备 getchar()&#xff1a;获取字符 int ch getchar(); //把获取的字符的ASCII码值放在ch中 int main() {int ch getchar();printf("%c\n", ch); //ch存的是该字符的ASCII码值&#xff0c;此处以字符形式打印ASCII码值对应的字符putchar(ch); } 运…

java IO 04 对象处理流,序列化

01.序列化和反序列化的作用 重点&#xff1a; 图&#xff1a; 02.对象流ObjectOutputStream和ObjectInputStream ObjectInputStream&#xff1a; ObjectOutputStream&#xff1a; 例子&#xff1a; 例子&#xff1a; 修改要序列化类的话&#xff0c;会出现不同的uid…

sentinel熔断降级

熔断降级 Slot 责任链上的最后一环&#xff1a;熔断降级 DegradeSlot,熔断降级作为保护系统的一种强大手段,可以根据慢调用、异常比例和异常数进行熔断,并自定义持续时间以实现系统保护 规则配置 规则类中属性解析 与控制面板对应 // 其中资源名称在 AbstractRule 里。 pu…

章节2:单词本该这样记

为什么我们记不住单词&#xff1f; 单词不是被胡编乱造出来的&#xff0c;单词是有规律的&#xff0c;单词是符合人类的逻辑的。 单词实际意思结构意义历史文化 我们要怎么记单词&#xff1f; 掌握单词的结构规律了解与单词有关的历史文化灵活巧计&#xff0c;不要太拘泥于…

软考78-上午题-【面向对象技术3-设计模式】-结构型设计模式01

一、适配器模式 1-1、意图 个类的接口转换成客户希望的另外一个接口。 Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 1-2、结构 适配器模式分为&#xff1a; 1、适配器类模式&#xff1b; 2、适配器对象模式 类适配器使用多重继承对一个接口与另…

Java微服务分布式事务框架seata

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Search)

搜索框组件&#xff0c;适用于浏览器的搜索内容输入框等应用场景。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Search(options?: { value?: string, placeholder?: Reso…