[USACO09JAN]安全出行Safe Travel 最短路,并查集
题目描述
Gremlins have infested the farm. These nasty, ugly fairy-like
creatures thwart the cows as each one walks from the barn (conveniently located at pasture_1) to the other fields, with cow_i traveling to from pasture_1 to pasture_i. Each gremlin is personalized and knows the quickest path that cow_i normally takes to pasture_i. Gremlin_i waits for cow_i in the middle of the final cowpath of the quickest route to pasture_i, hoping to harass cow_i.
Each of the cows, of course, wishes not to be harassed and thus chooses an at least slightly different route from pasture_1 (the barn) to pasture_i.
Compute the best time to traverse each of these new not-quite-quickest routes that enable each cow_i that avoid gremlin_i who is located on the final cowpath of the quickest route from pasture_1 to
pasture_i.
As usual, the M (2 <= M <= 200,000) cowpaths conveniently numbered 1..M are bidirectional and enable travel to all N (3 <= N <= 100,000) pastures conveniently numbered 1..N. Cowpath i connects pastures a_i (1 <= a_i <= N) and b_i (1 <= b_i <= N) and requires t_i (1 <= t_i <= 1,000) time to traverse. No two cowpaths connect the same two pastures, and no path connects a pasture to itself (a_i != b_i). Best of all, the shortest path regularly taken by cow_i from pasture_1 to pasture_i is unique in all the test data supplied to your program.
By way of example, consider these pastures, cowpaths, and [times]:
1--[2]--2-------+
| | |
[2] [1] [3]
| | |
+-------3--[4]--4
TRAVEL BEST ROUTE BEST TIME LAST PATH
p_1 to p_2 1->2 2 1->2
p_1 to p_3 1->3 2 1->3
p_1 to p_4 1->2->4 5 2->4
When gremlins are present:
TRAVEL BEST ROUTE BEST TIME AVOID
p_1 to p_2 1->3->2 3 1->2
p_1 to p_3 1->2->3 3 1->3
p_1 to p_4 1->3->4 6 2->4
For 20% of the test data, N <= 200.
For 50% of the test data, N <= 3000.
TIME LIMIT: 3 Seconds
MEMORY LIMIT: 64 MB
Gremlins最近在农场上泛滥,它们经常会阻止牛们从农庄(牛棚_1)走到别的牛棚(牛_i的目的 地是牛棚_i).每一个gremlin只认识牛_i并且知道牛_i一般走到牛棚_i的最短路经.所以它 们在牛_i到牛棚_i之前的最后一条牛路上等牛_i. 当然,牛不愿意遇到Gremlins,所以准备找 一条稍微不同的路经从牛棚_1走到牛棚_i.所以,请你为每一头牛_i找出避免gremlin_i的最 短路经的长度.
和以往一样, 农场上的M (2 <= M <= 200,000)条双向牛路编号为1..M并且能让所有牛到 达它们的目的地, N(3 <= N <= 100,000)个编号为1..N的牛棚.牛路i连接牛棚a_i (1 <= a_i <= N)和b_i (1 <= b_i <= N)并且需要时间t_i (1 <=t_i <= 1,000)通过. 没有两条牛路连接同样的牛棚,所有牛路满足a_i!=b_i.在所有数据中,牛_i使用的牛棚_1到牛 棚_i的最短路经是唯一的.
输入输出格式
输入格式:
* Line 1: Two space-separated integers: N and M
* Lines 2..M+1: Three space-separated integers: a_i, b_i, and t_i
输出格式:
* Lines 1..N-1: Line i contains the smallest time required to travel from pasture_1 to pasture_i+1 while avoiding the final cowpath of the shortest path from pasture_1 to pasture_i+1. If no such path exists from pasture_1 to pasture_i+1, output -1 alone on the line.
输入输出样例
4 5 1 2 2 1 3 2 3 4 4 3 2 1 2 4 3
3 3 6
提交地址 : Luogu2934
题意:
给你一些点, 他们与节点1的最短路的最后一条边不可走, 求每一个点到1的最短距离;
这道题很折磨人...
首先我们考虑一颗最短路径树(就是1到每个节点的最短路径路过的边构成的集合);
然后我们要找一个点对(x, y),且xy之间有边,xy不在最短路径树上,y在节点i的子树里,x不在节点i的子树里;
这样,我们要找的节点i的"非最短路径",就是dis(x) + dis(y) - dis(i) + w(x, y);
其中, dis代表节点到根节点的最短路径, w(x, y)代表x 到 y 的路径长度;
我们发现 : dis(i) 是确定的, 所以我们要最小化dis(x) + dis(y) + w(x, y);
我们可以用左偏树维护(才不会写) 想学戳这里
我直接排序;
选出不在最短路径树上的边, 按dis(x) + dis(y) + w(x, y)最小sort一遍;
然后更新答案;
我们接着想 : 一个点如果已经被更新, 那么一定是最优的, 为什么, 因为他的dis值确定, 而我们已经对以上那一坨式子的值按由小到大排序,所以我们第一次更新一定是他的最优值;
所以我们可以用并查集维护, 确保他们只被更新一次;
那么我们再想 : 一个点对(x, y),能更新那些点的ans呢?
能更新到x -> LCA(x, y) 和 y -> LCA(x, Y) 的所有点;
为什么?
因为 边 x-y 一定不是点i的父亲边 (题面说了), 如果我们更新到LCA(x, y) 那么x, y就在同一个子树里了;
而我们能更新i的前提是x, y不在同一子树里;
所以我们就像跳LCA一样一直往上跳, 一路更新答案, 然后修改并查集, 直到调到LCA;
应该比较好懂;
卡SPFA?差评!!
代码奉上:
//By zZhBr #include <iostream> #include <cstdio> #include <algorithm> #include <queue> #include <cstring> #include <queue> using namespace std; const int N = 100010; const int M = 400010; inline int read() { int res = 0; bool flag = 0; char c = getchar(); while (c < '0' or c > '9') { if (c == '-') flag = 1; c = getchar(); } while (c >= '0' and c <= '9') { res = res * 10 + c - '0'; c = getchar(); } return flag ? -res : res; } int n, m; int A[M], B[M], C[M]; int what[M];//第几条边属于的编号 what[i] = what[i^1]; int is[M]; //记录最短路径中第i个点的入边 bool itis[M];//记录是否是最短路径树的树边 int fafa[N];//记录在最短路径树中一个节点的父亲 int fa[N];//并查集 int ans[N];//记录答案 inline int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); } struct edge { int nxt; int to; int val; }ed[M]; int head[N], cnt; inline void add(int x, int y, int z, int gg) { cnt++; ed[cnt].nxt = head[x]; ed[cnt].to = y; ed[cnt].val = z; head[x] = cnt; what[cnt] = gg; } inline void add(int x, int y) { cnt++; ed[cnt].nxt = head[x]; ed[cnt].to = y; head[x] = cnt; } struct date { int x; int y; int w; // date(){} // date(int xx, int yy, int ww){x = xx, y = yy, w = ww;} }cf[M]; bool cmp(date a, date b) { return a.w < b.w; } void dfs(int x) //简陋的寻找父亲的函数 { for (register int i = head[x] ;i ; i = ed[i].nxt) { int to = ed[i].to; if (to == fafa[x]) continue; fafa[to] = x; dfs(to); } } /*.............................Dijkstra...................................*/ struct dij{int x, w;}; bool operator <(const dij &a,const dij &b) { return a.w > b.w; } int dis[N]; bool vis[N]; inline void Dijkstra(int haha) { memset(dis, 0x3f, sizeof dis); priority_queue < dij > q; dis[haha] = 0; q.push((dij){haha, 0}); while (!q.empty()) { dij t = q.top(); q.pop(); int x = t.x; if (vis[x]) continue; vis[x] = 1; for (register int i = head[x] ; i ; i = ed[i].nxt) { int to = ed[i].to; if (!vis[to] and dis[to] > dis[x] + ed[i].val) //! { is[to] = what[i]; dis[to] = dis[x] + ed[i].val; q.push((dij){to, dis[to]}); } } } } /*.................................End..............................................*/ int main() { n = read(), m = read(); for (register int i = 1 ; i <= m ; i ++) { A[i] = read(), B[i] = read(), C[i] = read(); add(A[i], B[i], C[i], i); add(B[i], A[i], C[i], i); } Dijkstra(1); cnt = 0; memset(head, 0, sizeof head); // for (int i=2;i<=n;i++)cout<<is[i]<<endl; for (register int i = 2 ; i <= n ; i ++) { int x = is[i]; add(A[x], B[x]); add(B[x], A[x]); itis[x] = 1; } dfs(1); int num = 0; for (register int i = 1 ; i <= m ; i ++) //该搞非树边了 { if (itis[i]) continue; // printf("%d\n", i); cf[++num] = (date) {A[i], B[i], dis[A[i]] + dis[B[i]] + C[i]}; } sort (cf + 1, cf + 1 + num, cmp); for (register int i = 1 ; i <= n ; i ++) fa[i] = i, ans[i] = -1; for (register int i = 1 ; i <= num ; i ++) { int x = cf[i].x, y = cf[i].y; x = Find(x), y = Find(y); //printf("x = %d, y = %d\n", x, y); while (x != y) { if (dis[x] < dis[y]) swap(x, y); ans[x] = cf[i].w - dis[x]; fa[x] = fafa[x]; x = Find(x); } } for (register int i = 2 ; i <= n ; i ++) { printf("%d\n", ans[i]); } return 0; } zZhBr
可以转载, 但请注明地址谢谢;