[BZOJ 1576] [Usaco2009 Jan] 安全路经Travel 【树链剖分】
题目链接: BZOJ - 1576
题目分析
首先Orz Hzwer的题解。
先使用 dijikstra 求出最短路径树。
那么对于一条不在最短路径树上的边 (u -> v, w) 我们可以先沿树边从 1 走到 u ,再走这条边到 v ,然后再沿树边向上,可以走到 (LCA(u, v), v] 的所有点 (不包括LCA(u, v)!!)。
对于一个属于 (LCA(u, v), v] 的点 x,这种走法的距离为 d[u] + w + d[v] - d[x] ,那么我们就可以用 d[u] + w + d[v] 更新 (LCA(u, v), v] 这一段点的权值,使用树链剖分 + 线段树。
枚举每一条非树边进行更新。
最后每个点 x 的答案就是 x 的权值 - d[x] 。
注意!LCA(u, v) 是不能被这条边更新的!
代码
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <cstdlib> #include <algorithm> #include <queue> using namespace std; const int MaxN = 100000 + 5, MaxM = 200000 + 5, MaxLog = 20, INF = 999999999; int n, m, Index; int Father[MaxN], Depth[MaxN], Top[MaxN], Size[MaxN], Son[MaxN], Pos[MaxN]; int d[MaxN], D[MaxN * 4], Jump[MaxN][MaxLog + 3]; struct Edge { int u, v, w; bool Mark; Edge *Next; } E[MaxM * 2], *P = E, *Pre[MaxN], *Point[MaxN]; inline void AddEdge(int x, int y, int z) { ++P; P -> u = x; P -> v = y; P -> w = z; P -> Mark = false; P -> Next = Point[x]; Point[x] = P; } struct ES { int x, y; ES() {} ES(int a, int b) { x = a; y = b; } }; struct Cmp { bool operator () (ES a, ES b) { return a.y > b.y; } }; priority_queue<ES, vector<ES>, Cmp> Q; bool Visit[MaxN]; void Dijkstra() { while (!Q.empty()) Q.pop(); for (int i = 1; i <= n; ++i) { d[i] = INF; Visit[i] = false; } d[1] = 0; for (int i = 1; i <= n; ++i) Q.push(ES(i, d[i])); ES Now; int x; while (!Q.empty()) { Now = Q.top(); Q.pop(); x = Now.x; if (Visit[x]) continue; Visit[x] = true; for (Edge *j = Point[x]; j; j = j -> Next) { if (d[x] + (j -> w) < d[j -> v]) { d[j -> v] = d[x] + j -> w; if (Pre[j -> v] != NULL) Pre[j -> v] -> Mark = false; Pre[j -> v] = j; j -> Mark = true; Q.push(ES(j -> v, d[j -> v])); } } } } int DFS_1(int x, int Dep, int Fa) { Depth[x] = Dep; Father[x] = Fa; Size[x] = 1; int SonSize, MaxSonSize; SonSize = MaxSonSize = 0; for (Edge *j = Point[x]; j; j = j -> Next) { if (j -> v == Fa || j -> Mark == false) continue; SonSize = DFS_1(j -> v, Dep + 1, x); if (SonSize > MaxSonSize) { MaxSonSize = SonSize; Son[x] = j -> v; } Size[x] += SonSize; } return Size[x]; } void DFS_2(int x) { if (x == 0) return; if (x == Son[Father[x]]) Top[x] = Top[Father[x]]; else Top[x] = x; Pos[x] = ++Index; DFS_2(Son[x]); for (Edge *j = Point[x]; j; j = j -> Next) { if (j -> v == Father[x] || j -> v == Son[x] || j -> Mark == false) continue; DFS_2(j -> v); } } void Build_Tree(int x, int s, int t) { D[x] = INF; if (s == t) return; int m = (s + t) >> 1; Build_Tree(x << 1, s, m); Build_Tree(x << 1 | 1, m + 1, t); } void Init_LCA() { for (int i = 1; i <= n; ++i) Jump[i][0] = Father[i]; for (int j = 1; j <= MaxLog; ++j) { for (int i = 1; i <= n; ++i) { if (Depth[i] < (1 << j)) continue; Jump[i][j] = Jump[Jump[i][j - 1]][j- 1]; } } } int LCA(int x, int y) { int Dif; if (Depth[x] < Depth[y]) swap(x, y); Dif = Depth[x] - Depth[y]; if (Dif) { for (int i = 0; i <= MaxLog; ++i) { if (Dif & (1 << i)) x = Jump[x][i]; } } if (x == y) return x; for (int i = MaxLog; i >= 0; --i) { if (Jump[x][i] != Jump[y][i]) { x = Jump[x][i]; y = Jump[y][i]; } } return Father[x]; } inline int gmin(int a, int b) {return a < b ? a : b;} void Paint(int x, int Num) { if (Num >= D[x]) return; D[x] = Num; } void PushDown(int x) { if (D[x] == INF) return; Paint(x << 1, D[x]); Paint(x << 1 | 1, D[x]); D[x] = INF; } void Change(int x, int s, int t, int l, int r, int Num) { if (l <= s && r >= t) { Paint(x, Num); return; } PushDown(x); int m = (s + t) >> 1; if (l <= m) Change(x << 1, s, m, l, r, Num); if (r >= m + 1) Change(x << 1 | 1, m + 1, t, l, r, Num); } void EChange(int x, int y, int z) { int fx, fy; fx = Top[x]; fy = Top[y]; while (fx != fy) { Change(1, 1, n, Pos[fx], Pos[x], z); x = Father[fx]; fx = Top[x]; } if (x != y) Change(1, 1, n, Pos[y] + 1, Pos[x], z); } int Get(int x, int s, int t, int p) { if (s == t) return D[x]; PushDown(x); int m = (s + t) >> 1; int ret; if (p <= m) ret = Get(x << 1, s, m, p); else ret = Get(x << 1 | 1, m + 1, t, p); return ret; } int main() { scanf("%d%d", &n, &m); int a, b, c; for (int i = 1; i <= m; ++i) { scanf("%d%d%d", &a, &b, &c); AddEdge(a, b, c); AddEdge(b, a, c); } Dijkstra(); DFS_1(1, 0, 0); Index = 0; DFS_2(1); Build_Tree(1, 1, n); Init_LCA(); int t; for (Edge *j = E + 1; ; ++j) { if (j -> Mark) continue; t = LCA(j -> u, j -> v); EChange(j -> v, t, d[j -> u] + j -> w + d[j -> v]); if (j == P) break; } int Temp; for (int i = 2; i <= n; ++i) { Temp = Get(1, 1, n, Pos[i]); if (Temp < INF) printf("%d\n", Temp - d[i]); else printf("-1\n"); } return 0; }