[BZOJ 1576] 安全路径 最短路径树 路径压缩
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[BZOJ 1576] 安全路径 最短路径树 路径压缩相关的知识,希望对你有一定的参考价值。
题意
给定一张 n 个点 m 条边的图, 保证对于任意的点 i , 从点 1 到点 i 的最短路唯一.
对于任意的点 i , 询问: 将 1 到 i 的最短路中最后一条边删去之后, 从 1 到 i 的最短路 .
n <= 100000, m <= 200000 .
分析
首先跑 Dijsktra , 构建出最短路径树.
接下来考虑每条非树边 E[p] = (u, v, d) 对答案的影响, 它能且仅能影响到点 u, v 之上, LCA(u, v) 之下的点的答案. (包括 u, v, 不包括 LCA(u, v)) .
假装这个点是 x , 那么可以得到一条长度为 d[u] + d[v] + d - d[x] 的路径.
设 val = d[u] + d[v] + d , 那么 ans[x] = min(val - d[x]) = min(val) - d[x] .
现在的问题被抽象为: 对于一棵树, 若干次 [ 某条路径被某个权值覆盖 ] , 求每个点上的最小权值.
我们仔细想一想, 发现直接先将所有非树边按照 val 从小到大排序, 然后用路径压缩均摊到 O(n) .
实现
每个点只会被赋值一次, 只要能够快速跳到上一个没有赋值的点, 并进行赋值 .
所以我想了想, 做法大致可以被描述为: 进行路径压缩的同时, 维护答案.
Fill(x, y, val) , 返回 x 和 y 的 LCA , 或者 LCA 所在集合的代表元.
当 x = y 时, 找到了 LCA 或者 LCA 所在集合的代表元, 直接返回 x .
否则说明至少有一个点还没有到达 LCA , 深度大的那个点一定没有到达 LCA . 若 dep[x] < dep[y] , 交换 x, y .
若点 x 还没有被染色, 那么染成 val .
将 x 往上跳, 并维护路径压缩的结构, par[x] = Fill(par[x], y, val) .
1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include <algorithm> 6 #include <queue> 7 using namespace std; 8 #define F(i, a, b) for (register int i = (a); i <= (b); i++) 9 inline int rd(void) { 10 int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1; 11 int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f; 12 } 13 14 const int N = 100005; 15 const int M = 400005; 16 const int INF = ~0u>>2; 17 18 #define fore(k, x) for (register int k = hd[x]; k > 0; k = nx[k]) 19 int n, m, hd[N], nx[M], tot, adj[M], cost[M]; 20 inline void Init(int x, int y, int d) { nx[++tot] = hd[x], hd[x] = tot, adj[tot] = y, cost[tot] = d; } 21 22 int d[N], dep[N], par[N]; priority_queue< pair<int, int> > q; 23 void Dij(void) { 24 fill(d+1, d+n+1, INF), q.push(make_pair(0, 1)); 25 while (!q.empty()) { 26 pair<int, int> cur = q.top(); q.pop(); 27 if (d[cur.second] != INF) continue; 28 d[cur.second] = -cur.first; 29 fore(k, cur.second) 30 if (d[adj[k]] == INF) 31 q.push(make_pair(cur.first - cost[k], adj[k])); 32 } 33 } 34 void Prework(int x) { 35 fore(k, x) if (d[x] + cost[k] == d[adj[k]]) 36 dep[adj[k]] = dep[x]+1, par[adj[k]] = x, Prework(adj[k]); 37 } 38 39 int idx, ans[N]; 40 struct Edge { 41 int x, y, val; 42 friend inline bool operator < (Edge A, Edge B) { return A.val < B.val; } 43 }e[M]; 44 45 inline int Find(int x, int y, int val) { 46 if (x == y) return x; 47 if (dep[x] < dep[y]) swap(x, y); 48 if (!ans[x]) ans[x] = val - d[x]; 49 return par[x] = Find(par[x], y, val); 50 } 51 52 int main(void) { 53 #ifndef ONLINE_JUDGE 54 freopen("bzoj1576.in", "r", stdin); 55 #endif 56 57 n = rd(), m = rd(); 58 F(i, 1, m) { 59 int a = rd(), b = rd(), t = rd(); 60 Init(a, b, t), Init(b, a, t); 61 } 62 63 Dij(); 64 Prework(1); 65 66 F(x, 1, n) fore(k, x) 67 if (d[x] + cost[k] != d[adj[k]] && d[adj[k]] + cost[k] != d[x] && x < adj[k]) 68 e[++idx] = (Edge){x, adj[k], cost[k] + d[x] + d[adj[k]]}; 69 sort(e+1, e+idx+1); 70 71 F(i, 1, idx) 72 Find(e[i].x, e[i].y, e[i].val); 73 F(i, 2, n) printf("%d\n", !ans[i] ? -1 : ans[i]); 74 75 return 0; 76 }
以上是关于[BZOJ 1576] 安全路径 最短路径树 路径压缩的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ1576 [Usaco2009 Jan]安全路经Travel