BZOJ 1576: [Usaco2009 Jan]安全路经Travel
日常自闭半小时后看题解,太弱了qwq。
感觉这道题还是比较难的,解法十分巧妙,不容易想到。
首先题目说了起点到每个点的最短路都是唯一的,那么对这个图求最短路图必定是一棵树,而且这棵树是唯一的。
那么我们求出了这棵最短路树,每当删掉一条边,其实这条边就是这个点与它父亲结点的连边,那么我们肯定是会 选择一些非树边连上这个点 让这个点连回最短路树。那么我们倒过来考虑,想想每一条非树边能对答案造成什么贡献?
当一条非树边(x,y)连上了最短路树,会与x,y的LCA形成一个环,而且 P = dis[x]+dis[y]+w = 2*dis[LCA(x,y)]+环长度 这似乎和答案没什么关系,其实 P-dis[y] 就是 ans[x]的一个答案选择,同理P-dis[x]也是ans[y]的一个选择(选择的意思就是一条满足条件的路径,不经过被删边)。那么我们可以得到一个朴素算法:枚举每条非树边,计算所有的ans[x]和ans[y]的选择,然后选一个最小的即可。
显然朴素算法会超时。那么这里就用到一个十分巧妙的方法:观察上式我们发现都有一个共同的东西P=dis[x]+dis[y]+w 。然后这个东西再减去dis[x]或者dis[y]。那么对于某个点x,既然减去的东西一样都是ans=min(P-dis[y]) 那么显然当P最小的时候,答案有最优解。
那么我们得到这样一个算法,先按P=dis[x]+dis[y]+w排序,按P从小到大去更新答案,这样就不用暴力更新找最小值。
而且按上诉方式得到的答案是最优的,那么每个点只要更新一次就好了,所以我们还可以用树上的并查集加速这个非树边向上跳跃更新答案的过程。(就是当对于点x更新完后,把x的并查集合并到x的父亲结点)。
至此问题得到解决。
细节详见代码:
- #include<bits/stdc++.h>
- using namespace std;
- typedef pair<int,int> pii;
- const int N=2e5+;
- int n,m,tot,x[N],y[N],z[N],dis[N],fa[N],pre[N],ans[N];
- struct edge{
- int x,y,z;
- bool operator < (const edge &rhs) const {
- return dis[x]+dis[y]+z<dis[rhs.x]+dis[rhs.y]+rhs.z;
- }
- }e[N<<];
- int cnt=,head[N],to[N<<],nxt[N<<],len[N<<];
- void add_edge(int x,int y,int z) {
- nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt;
- }
- bool vis[N];
- priority_queue<pii> q;
- void Dijkstra() {
- memset(dis,0x3f,sizeof(dis));
- memset(vis,,sizeof(vis));
- dis[]=; q.push(make_pair(,));
- while (!q.empty()) {
- pii u=q.top(); q.pop();
- if (vis[u.second]) continue;
- vis[u.second]=;
- for (int i=head[u.second];i;i=nxt[i]) {
- int y=to[i];
- if (dis[y]>dis[u.second]+len[i]) {
- dis[y]=dis[u.second]+len[i];
- q.push(make_pair(-dis[y],y));
- }
- }
- }
- }
- int getfa(int x) { return x==fa[x] ? x : fa[x]=getfa(fa[x]); }
- void solve(int x,int y,int w) { //树上并查集
- while (getfa(x)!=getfa(y)) {
- x=getfa(x); y=getfa(y); //这一步很重要
- if (dis[x]>dis[y]) swap(x,y);
- ans[y]=w-dis[y];
- fa[y]=fa[pre[y]];
- y=pre[y]; //交替向上跳
- }
- }
- int main()
- {
- cin>>n>>m;
- for (int i=;i<=m;i++) {
- scanf("%d%d%d",&x[i],&y[i],&z[i]);
- add_edge(x[i],y[i],z[i]); add_edge(y[i],x[i],z[i]);
- }
- Dijkstra();
- for (int i=;i<=m;i++)
- if (dis[x[i]]+z[i]==dis[y[i]]) pre[y[i]]=x[i];
- else if (dis[y[i]]+z[i]==dis[x[i]]) pre[x[i]]=y[i];
- else { e[++tot].x=x[i]; e[tot].y=y[i]; e[tot].z=z[i]; }
- sort(e+,e+tot+);
- for (int i=;i<=n;i++) fa[i]=i;
- for (int i=;i<=tot;i++)
- solve(e[i].x,e[i].y,dis[e[i].x]+dis[e[i].y]+e[i].z);
- for (int i=;i<=n;i++) printf("%d\n",ans[i] ? ans[i] : -);
- return ;
- }
BZOJ 1576: [Usaco2009 Jan]安全路经Travel的更多相关文章
- bzoj 1576: [Usaco2009 Jan]安全路经Travel 树链剖分
1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec Memory Limit: 64 MB Submit: 665 Solved: 227[Sub ...
- [BZOJ 1576] [Usaco2009 Jan] 安全路经Travel 【树链剖分】
题目链接: BZOJ - 1576 题目分析 首先Orz Hzwer的题解. 先使用 dijikstra 求出最短路径树. 那么对于一条不在最短路径树上的边 (u -> v, w) 我们可以先沿 ...
- bzoj 1576 [Usaco2009 Jan]安全路经Travel(树链剖分,线段树)
[题意] 给定一个无向图,找到1-i所有的次短路经,要求与最短路径的最后一条边不重叠. [思路] 首先用dijkstra算法构造以1为根的最短路树. 将一条无向边看作两条有向边,考察一条不在最短路树上 ...
- BZOJ.1576.[Usaco2009 Jan]安全路经Travel(树形DP 并查集)
题目链接 BZOJ 洛谷 先求最短路树.考虑每一条非树边(u,v,len),设w=LCA(u,v),这条边会对w->v上的点x(x!=w)有dis[u]+dis[v]-dis[x]+len的距离 ...
- bzoj 1576: [Usaco2009 Jan]安全路经Travel——并查集+dijkstra
Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数 ...
- bzoj 1576: [Usaco2009 Jan]安全路经Travel【spfa+树链剖分+线段树】
这几天写USACO水题脑子锈住了--上来就贪心,一交就WA 事实上这个是一个叫最短路树的东西,因为能保证只有一条最短路,所以所有最短路合起来是一棵以1为根的树,并且在这棵树上,每个点被精灵占据的路是它 ...
- 【BZOJ】1576 [Usaco2009 Jan]安全路经Travel
[算法]最短路树+(树链剖分+线段树)||最短路树+并查集 [题解] 两种方法的思想是一样的,首先题目限制了最短路树唯一. 那么建出最短路树后,就是询问对于每个点断掉父边后重新找路径的最小值,其它路径 ...
- 【BZOJ1576】[Usaco2009 Jan]安全路经Travel 最短路+并查集
[BZOJ1576][Usaco2009 Jan]安全路经Travel Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, ...
- 【思维题 并查集 图论】bzoj1576: [Usaco2009 Jan]安全路经Travel
有趣的思考题 Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第 ...
随机推荐
- number框
因为系统的number框无法设置样式,所以休息无聊时写了一个简单的模拟number框的插件,效果不是很完善,有一些功能可能没注意到 // 简单的模拟number框插件 // 布局: // <di ...
- 使用extract-text-webpack-plugin处理css文件路径问题
首先看到我们的文件夹目录如下: webpack.config.js //解析分离cssconst ExtractTextPlugin = require('extract-text-webpack-p ...
- exp ORA-01455: converting column overflows integer datatype
EXP-00008: ORACLE error 1455 encounteredORA-01455: converting column overflows integer datatype add ...
- IDA技巧
3. 用[shift+F12]调出字符串表,再根据这些字符串,查看引用的地方,也可以分析出一些sub_0XXXX函数的功能. 进入import函数内 x查看调用处 jump to xref 跳到引用 ...
- paper 135:关于C#泛型的知识点
计划着要用一个月的时间把 C#语言Windows程序设计 搞定,现在是零零散散的知识点,日积月累吧!朋友们,看这里咯~呵呵 原文地址:http://www.blogjava.net/Jack2007 ...
- linux教程及常用命令手册
Linux 教程 Linux 教程.Linux 简介.Linux 安装.Linux 系统启动过程.Linux 系统目录结构.Linux 忘记密码解决方法.Linux 远程登录.Linux 文件基本属性 ...
- php开发面试题---攻击网站的常用手段有哪些,及如何预防(整理)
php开发面试题---攻击网站的常用手段有哪些,及如何预防(整理) 一.总结 一句话总结: 比较记忆:注意比较各种攻击的区别,比如csrf和xss,以及xss和sql,这样才能记住 1.Sql注入是什 ...
- Ruby 技能图谱
# Ruby 技能图谱 说明: 本图谱只捡重点的列举,并非包含全部.文中所列举或没有列举的资源信息都可以在[awesome-ruby](https://github.com/markets/aweso ...
- appium 链接真机运行时报错
今天用appium链接真机时,碰到的第一个问题:Attempt to re-install io.appium.settings without first uninstalling.(这是日志中显示 ...
- 如何用python读写CSV 格式文件
工作中经常会碰到读写CSV文件的情况.记录下,方便自己以后查询并与大家一起分享: 写CSV文件方法一: import csv #导入CSV with open("D:\eg ...