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行: 第 ...
随机推荐
- 使用aop和BindingResult进行参数验证
1.在需要校验的参数名上面添加注解 2.在web层接收参数(参数前面使用@Valid进行标记,后面必须紧跟参数bindingResult,存储参数的错误信息) 3.使用aop进行校验信息统一处理 @C ...
- hdu4857 拓扑排序
题目大意: 糟糕的事情发生啦,现在大家都忙着逃命.但是逃命的通道很窄,大家只能排成一行. 现在有n个人,从1标号到n.同时有一些奇怪的约束条件,每个都形如:a必须在b之前. 同时,社会是不平等的,这些 ...
- jsp获取url路径的方法
如果你请求的URL是 http://localhost:8080/demo/Index.jsp request.getScheme() //输出:http request.getServerName ...
- ATM&购物商城程序
模拟实现一个ATM + 购物商城程序 额度15000或自定义 实现购物商城,买东西加入购物车,调用信用卡接口转账 可以体现,手续费5% 支持多账户登录 支持账户间转账 记录每月日常消费流水 提供还款接 ...
- 【InnoDB】插入缓存,两次写,自适应hash索引
InnoDB存储引擎的关键特性包括插入缓冲.两次写(double write).自适应哈希索引(adaptive hash index).这些特性为InnoDB存储引擎带来了更好的性能和更高的可靠性. ...
- vue 前后端分离 接口及result规范 及drf安装使用方法
接口 # 接口:url链接,通过向链接发送不同的类型请求与参数得到相应的响应数据# 1.在视图层书写处理请求的 视图函数# 2.在路由层为视图函数配置 url链接 => 产生接口# 3.前台通 ...
- Python基础(二):斐波那契数列、模拟cp操作、生成8位随机密码
一.斐波那契数列 目标: 编写fib.py脚本,主要要求如下: 输出具有10个数字的斐波那契数列 使用for循环和range函数完成 改进程序,要求用户输入一个数字,可以生成用户需要长度的斐波那契数列 ...
- AcWing 214. Devu和鲜花 (容斥)打卡
Devu有N个盒子,第i个盒子中有AiAi枝花. 同一个盒子内的花颜色相同,不同盒子内的花颜色不同. Devu要从这些盒子中选出M枝花组成一束,求共有多少种方案. 若两束花每种颜色的花的数量都相同,则 ...
- 不是有效的win32应用程序
问题描述: 用vs2012编写的程序在xp下运行提示"不是有效的win32应用程序", 改成静态编译还是会提示上面的错误 解决办法: 原来常规里面的平台工具集的设置如上,更改为下面 ...
- 58、salesforce学习笔记(五)
Set集合 Set<String> set1 = new Set<String>(); set1.add('1'); set1.add('2'); Set<String& ...