[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

BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治

[BZOJ4016][FJOI2014]最短路径树问题

bzoj4016 [FJOI2014]最短路径树问题

『USACO』安全路经Travel (bzoj 1576)

安全路径——最短路径树+dsu缩边