bzoj 1576: [Usaco2009 Jan]安全路经Travel
2016-04-27 16:24
357 查看
1576: [Usaco2009 Jan]安全路经Travel
Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 968 Solved: 330
[Submit][Status][Discuss]
Description
Input
* 第一行: 两个空格分开的数, N和M* 第2..M+1行: 三个空格分开的数a_i, b_i,和t_i
Output
* 第1..N-1行: 第i行包含一个数:从牛棚_1到牛棚_i+1并且避免从牛棚1到牛棚i+1最短路经上最后一条牛路的最少的时间.如果这样的路经不存在,输出-1.Sample Input
4 51 2 2
1 3 2
3 4 4
3 2 1
2 4 3
输入解释:
跟题中例子相同
Sample Output
33
6
输出解释:
跟题中例子相同
HINT
Source
Gold[Submit][Status][Discuss]
题解:最短路+并查集
用dijkstra的堆优化建立最短路树,并记录最短路上的边,每个节点的父亲节点和节点在树中的深度。
因为最短路树上的1-i路径中的最后一条边是不能通过的,所以就是在最短路树中加一条边,从另一个方向到底当前点
对于一条不在最短路树的边u-nw,长度v,设t=lca(u,nw)
那么对于t-u和t-nw 的路径上所有点x,都可通过先求出环上的长度,在减去dis[x]的长度求得。
路径长度为dis[u]+v+dis[nw]-dis[x],因为dis[x]的长度是固定的,所以最小化这个长度,也就是最小化dis[u]+v+dis[nw]
所以我们可以用这个值去更新t-u,t-nw路径中所有点(不包括t)的最短路长度。
比较好想的思路是树链剖分+线段树,用线段树去维护每个点的dis[u]+v+dis[nw],但是还有更优越的思路。
前面的都不变,只是我们这次不用线段树维护,而是先把所有不再最短路树中的边加到一个结构体中,然后按照dis[u]+v+dis[nw]从小到大排序,因为权值小的靠前,所有第一次更新的一定是最小的,这样的话每个点至多会被更新一次,那么我们可以把一条链上的点用并查集并到一起,并查集的编号就是树链顶端点的father,这样在求lca
上的路径时,算完一个点就直接蹦到她所属的链顶的上一个(即记录的fa[x]的值),这样每个点最多被计算一次。保证正确性的同时,也保证了科学的时间负责度。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<queue> #define pa pair<int,int> #define inf 1000000000 #define N 400003 using namespace std; int n,m; int tot,u ,vis ,point ,v ,next ,fa ,pre ,d ,mark ; int f ,nw ,ans ,num,deep ,dis ; struct data { int x,y,len; };data a ; void add(int x,int y,int z) { tot++; next[tot]=point[x]; point[x]=tot; u[tot]=y; v[tot]=z; nw[tot]=x; tot++; next[tot]=point[y]; point[y]=tot; u[tot]=x; v[tot]=z; nw[tot]=y; } int cmp(data a,data b) { return a.len<b.len; } void dijkstra() { priority_queue<pa,vector<pa>,greater<pa> > q; for (int i=1;i<=n;i++) dis[i]=inf; dis[1]=0; deep[1]=1; q.push(make_pair(0,1)); while(!q.empty()) { int now=q.top().second; q.pop(); if (vis[now]) continue; vis[now]=1; for (int i=point[now];i;i=next[i]) if (dis[now]+v[i]<dis[u[i]]) { dis[u[i]]=dis[now]+v[i]; mark[pre[u[i]]]=0; pre[u[i]]=i; deep[u[i]]=deep[now]+1; mark[i]=1; f[u[i]]=now; q.push(make_pair(dis[u[i]],u[i])); } } } int find(int x) { if (fa[x]==x) return x; fa[x]=find(fa[x]); return fa[x]; } void solve(int u,int v,int k) { int x=find(u); int y=find(v); while (x!=y) { if (deep[x]<deep[y]) swap(x,y); ans[x]=k-dis[x]; num++; fa[x]=f[x]; x=find(fa[x]); } } int main() { scanf("%d%d",&n,&m); for (int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); } dijkstra(); int cnt=0; for (int i=1;i<=m;i++) if (!mark[i*2-1]&&!mark[i*2]) { cnt++; a[cnt].x=nw[i*2]; a[cnt].y=u[i*2]; a[cnt].len=dis[nw[i*2]]+dis[u[i*2]]+v[i*2]; } sort(a+1,a+cnt+1,cmp); for (int i=1;i<=n;i++) ans[i]=-1,fa[i]=i; for (int i=1;i<=cnt;i++) { solve(a[i].x,a[i].y,a[i].len); if (num==n-1) break; } for (int i=2;i<=n;i++) printf("%d\n",ans[i]); }
相关文章推荐
- 函数指针,函数指针数组
- Jessite 常见问题解决
- docker 报错Failed to start Docker Storage Setup. 的处理基本都是容器满了
- iOS开发之.pch文件初识
- Fragment解析
- Vim常用操作(推荐)
- 慎用AsyncTask
- Android 插件换肤原理解析
- 遍历listmap 遍历map
- python-netcat
- iOS应用架构谈-网络层设计方案
- 【LeetCode】202. Happy Number
- 百度地图api申请密钥
- GeoQuiz 第二章挑战练习代码
- 适合于图像处理方向的SCI期刊杂志列表
- Java并发编程:进程和线程之由来
- 冲刺6
- Spire.Office组件使用例子
- 解决Gradle DSL method not found: ‘android()’
- MongoDB