日常自闭半小时后看题解,太弱了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的更多相关文章

  1. bzoj 1576: [Usaco2009 Jan]安全路经Travel 树链剖分

    1576: [Usaco2009 Jan]安全路经Travel Time Limit: 10 Sec  Memory Limit: 64 MB Submit: 665  Solved: 227[Sub ...

  2. [BZOJ 1576] [Usaco2009 Jan] 安全路经Travel 【树链剖分】

    题目链接: BZOJ - 1576 题目分析 首先Orz Hzwer的题解. 先使用 dijikstra 求出最短路径树. 那么对于一条不在最短路径树上的边 (u -> v, w) 我们可以先沿 ...

  3. bzoj 1576 [Usaco2009 Jan]安全路经Travel(树链剖分,线段树)

    [题意] 给定一个无向图,找到1-i所有的次短路经,要求与最短路径的最后一条边不重叠. [思路] 首先用dijkstra算法构造以1为根的最短路树. 将一条无向边看作两条有向边,考察一条不在最短路树上 ...

  4. 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的距离 ...

  5. bzoj 1576: [Usaco2009 Jan]安全路经Travel——并查集+dijkstra

    Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第i行包含一个数 ...

  6. bzoj 1576: [Usaco2009 Jan]安全路经Travel【spfa+树链剖分+线段树】

    这几天写USACO水题脑子锈住了--上来就贪心,一交就WA 事实上这个是一个叫最短路树的东西,因为能保证只有一条最短路,所以所有最短路合起来是一棵以1为根的树,并且在这棵树上,每个点被精灵占据的路是它 ...

  7. 【BZOJ】1576 [Usaco2009 Jan]安全路经Travel

    [算法]最短路树+(树链剖分+线段树)||最短路树+并查集 [题解] 两种方法的思想是一样的,首先题目限制了最短路树唯一. 那么建出最短路树后,就是询问对于每个点断掉父边后重新找路径的最小值,其它路径 ...

  8. 【BZOJ1576】[Usaco2009 Jan]安全路经Travel 最短路+并查集

    [BZOJ1576][Usaco2009 Jan]安全路经Travel Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, ...

  9. 【思维题 并查集 图论】bzoj1576: [Usaco2009 Jan]安全路经Travel

    有趣的思考题 Description Input * 第一行: 两个空格分开的数, N和M * 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i Output * 第1..N-1行: 第 ...

随机推荐

  1. 大哥带我走渗透4(中)----oracle报错注入

    5/30 报错注入 0x01 准备阶段 1. 基础知识今天了解了,但是,只能看懂和最基本的理解,不能自己上路.所以,还是要不停学习基础.并且及时总结.这有一篇很详细的文章:https://www.cn ...

  2. TCP/UDP Linux网络编程详解

    本文主要记录TCP/UDP网络编程的基础知识,采用TCP/UDP实现宿主机和目标机之间的网络通信. 内容目录 1. 目标2.Linux网络编程基础2.1 嵌套字2.2 端口2.3 网络地址2.3.1 ...

  3. win32程序使用CString

    https://www.cnblogs.com/qingtian224/p/5833456.html uafxcwd.lib(afxmem.obj) : error LNK2005: "vo ...

  4. React 滚动事件

    代码: <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UT ...

  5. 21.与重入锁相关联的Condition

    import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; /** * ...

  6. python reduce()函数使用

    reduce()的使用方法形如reduce(function, iterable[, initializer]),它的形式和map()函数一样.不过参数f(x)必须有两个参数,initializer是 ...

  7. WPF 依赖附加属性

    附加属性的本质是一个依赖属性,与普通的依赖属性区别: 1:注册方法名不一样,如 DependencyProperty.RegisterAttached 2:没有普通的属性包装器,而是通过get和set ...

  8. servlet项目找不到java文件(由于照抄代码造成的bug)

    其实这个bug非常简单,实际上是由于路径配置问题,也是由于我照抄代码造成的(好孩子不要学!) 情况是这样的:java文件没有问题但是启动项目的时候出现了bug: 然后当我把GetData.java和J ...

  9. computed和watch运用场景

    computed:通过属性计算而得来的属性 1.computed内部的函数在调用时不加(). 2.computed是依赖vm中data的属性变化而变化的,也就是说,当data中的属性发生改变的时候,当 ...

  10. Django 模型层关系映射

    一.一对一映射 1.什么是一对一 A表中的一条记录只能与B表中的一条记录相关联如:一夫一妻制 2.语法 允许在关联的两个类的任何一个类中 增加: 属性 = models.OneToOneField(E ...