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行: 第 ...
随机推荐
- until 循环语句
- Linux xargs 命令
xargs xargs 是给命令传递参数的一个过滤器,也是组合多个命令的一个工具. xargs 可以将管道或标准输入(stdin)数据转换成命令行参数,也能够从文件的输出中读取数据. xargs 也可 ...
- 使用canvas时, 如何用相对单位(rem, rpx)来适配不同机型
使用canvas的api时, 使用的都是绝对数值, 如: 方法传参是坐标位置,不带单位,如ctx.translate(10,10); 那么此时, 我就需要将rem或rpx 转换成 px; 首先, 获取 ...
- Jquery中的offset()和position()深入剖析(元素定位)
先看看这两个方法的定义. offset(): 获取匹配元素在当前视口的相对偏移. 返回的对象包含两个整形属性:top 和 left.此方法只对可见元素有效. position(): 获取匹配元素相对父 ...
- 关于Python实现Interface base64加解密方法
''' 以下Python Code运行环境为windows10, Python版本为3.5.3 涉及的库:base64,json,unittest ''' # coding=utf-8 # impor ...
- Maven介绍及安装
1.maven是一个管理第三方库的jar package 2.从该页面下载相应的Maven jar包(http://maven.apache.org/download.cgi),linux OS下 ...
- Java中"String.equals()“和"=="的区别
Do NOT use the `==`` operator to test whether two strings are equal! It only determines whether or n ...
- php abs()函数 语法
php abs()函数 语法 abs()函数怎么用? abs()函数的作用是返回一个数的绝对值.语法是abs(number),如果参数 number 是 float,则返回的类型也是 float,否则 ...
- hdu 4705 Y (树形dp)
Description Input 4 1 2 1 3 1 4 题目的意思是给你一棵树,让你找到所有不在一条路径上的三个点的情况个数.乍一看正向处理比较麻烦,我们从反方向考虑,如果是取在一条路径上的3 ...
- java %d %n \n
Java中,%d和%f分别用来表示输出时,替换整型输出和浮点型输出的占位符. 如: int a=28; float b = 13.0f; System.out.printf("整数是:%d% ...