日常自闭半小时后看题解,太弱了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. Map和List集合嵌套取值

    遍历List的方法: List<User> list = new ArrayList();list = userMapper.getUserList();//从数据库取得list值for( ...

  2. 【CSS3】transform-origin以原点进行旋转 (转)

    话不多说, 以左上角为原点 -moz-transform-origin: 0 0; -webkit-transform-origin:0 0; -o-transform-origin:0 0; 以右上 ...

  3. Java中Calendar类的常用方法(对时间进行计算的类)

    例子: /**** * 传入具体日期 ,返回具体日期增加一个月. * @param date 日期(2017-04-13) * @return 2017-05-13 * @throws ParseEx ...

  4. 理解Java的Class类、"this."关键字、Constructor构造器(一)

    import java.util.*; public class BookTest { public static void main(String[] args) { //Book book = n ...

  5. 【LeetCode 31】下一个排列

    题目链接 [题解] 从右往左找第一个下降的位置i(即满足nums[i]<nums[i+1]); 然后在[i+1..len-1]这个区间里面找到一个最大的下标k,使得nums[k]>nums ...

  6. mybatis中一对多查询collection关联不执行

    今天遇到的原因是因为下面红底id没有,导致关联查询没有条件(id字段没传),所以一直没有执行. <?xml version="1.0" encoding="UTF- ...

  7. http协议和file协议的区别

    1.在本地直接使用浏览器打开  html文件 和 通过本地服务器打开  html文件  有什么区别呢.  https://segmentfault.com/q/1010000006554881/a-1 ...

  8. tomcat部署项目遇到的问题

    场景:在一台服务器上部署多个Tomcat,每个Tomcat下运行各自的项目 * )启动Tomcat startup.cmd报错: java.lang.Exception: Socket bind fa ...

  9. Vuetify按需加载配置

    自己配置vuetify按需加载的步骤,在此记录: 执行npm install vuetify –save 或 yarn add vuetify添加vuetify添加依赖执行npm install -- ...

  10. python random模块随机取list中的某个值

    import random from random import randint ''' random.randint()随机生一个整数int类型,可以指定这个整数的范围,同样有上限和下限值,pyth ...