【题解】BZOJ-2125-最短路

题目

https://www.lydsy.com/JudgeOnline/problem.php?id=2125

题意

每次询问仙人掌上两点之间的最短路。

题解

  • 建圆方树。圆方边的长度是圆点到这个方点表示的环的根的最短距离(如果圆点是方点的父亲,这条边长度为 0)。
  • 然后 树剖 / 倍增 求两点间距离。但是如果 LCA 是方点的话,需要找出询问的两个点在 LCA 的哪个儿子的子树中,然后重新计算这两个儿子之间的最短距离。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
#define FOR(i, x, y) for (decay<decltype(y)>::type i = (x), _##i = (y); i < _##i; ++i)
#define FORD(i, x, y) for (decay<decltype(x)>::type i = (x), _##i = (y); i > _##i; --i)
#ifdef zerol
#define dbg(args...) do { cout << "\033[32;1m" << #args << " -> "; err(args); } while (0)
#else
#define dbg(...)
#endif
void err() { cout << "\033[39;0m" << endl; }
template<template<typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args) { for (auto x: a) cout << x << ' '; err(args...); }
template<typename T, typename... Args>
void err(T a, Args... args) { cout << a << ' '; err(args...); }
// -----------------------------------------------------------------------------
const int N = 2E5 + 100;

int n, nn;
namespace R {
struct E { int to, d; };
vector<E> G[N];
int len[N]; // 环的大小
bool lr[N]; // 环的左侧还是右侧
void addedge(int u, int v, int d) {
G[u].push_back({v, d}); G[v].push_back({u, d});
}
int fa[N], dep[N], idx[N], out[N], ridx[N], dd[N], D[N];
namespace hld {
int sz[N], son[N], top[N], clk;
void predfs(int u, int d) {
dep[u] = d; sz[u] = 1;
int& maxs = son[u] = -1;
for (E& e: G[u]) {
int v = e.to;
if (v == fa[u]) continue;
fa[v] = u; dd[v] = e.d; D[v] = D[u] + dd[v];
predfs(v, d + 1);
sz[u] += sz[v];
if (maxs == -1 || sz[v] > sz[maxs]) maxs = v;
}
}
void dfs(int u, int tp) {
top[u] = tp; idx[u] = ++clk; ridx[clk] = u;
if (son[u] != -1) dfs(son[u], tp);
for (E& e: G[u]) {
int v = e.to;
if (v != fa[u] && v != son[u]) dfs(v, v);
}
out[u] = clk;
}
int go(int u, int v) {
int uu = top[u], vv = top[v];
while (uu != vv) {
if (dep[uu] < dep[vv]) { swap(uu, vv); swap(u, v); }
u = fa[uu]; uu = top[u];
}
if (dep[u] < dep[v]) swap(u, v);
return v;
}
int finds(int u, int rt) {
while (top[u] != top[rt]) {
u = top[u];
if (fa[u] == rt) return u;
u = fa[u];
}
return ridx[idx[rt] + 1];
}
}
int psd[N];
void init() {
hld::clk = 0; hld::predfs(1, 1); hld::dfs(1, 1);
FOR (i, 1, nn + 1) psd[i] = dd[ridx[i]] + psd[i - 1];
}
int query(int u, int v) {
int lca = hld::go(u, v);
int ans = D[u] + D[v] - 2 * D[lca];
if (lca > n) {
int uu = hld::finds(u, lca), vv = hld::finds(v, lca);
ans -= dd[uu] + dd[vv];
if (lr[uu] == lr[vv]) ans += abs(dd[uu] - dd[vv]);
else ans += min(dd[uu] + dd[vv], len[lca] - dd[uu] - dd[vv]);
}
return ans;
}
}

namespace X {
struct E { int to, d, nxt; };
E e[N * 2];
int hd[N], ecnt;
void addedge(int u, int v, int d) {
e[ecnt] = {v, d, hd[u]}; hd[u] = ecnt++;
e[ecnt] = {u, d, hd[v]}; hd[v] = ecnt++;
}
int idx[N], clk, fa[N], dep[N];
bool ring[N];
void init() {
ecnt = clk = 0;
memset(hd, -1, sizeof hd);
memset(dep, 0, sizeof dep);
}
void dfs(int u, int feid) {
idx[u] = ++clk;
for (int i = hd[u]; ~i; i = e[i].nxt) {
if ((i ^ feid) == 1) continue;
int v = e[i].to;
if (!idx[v]) {
fa[v] = u; dep[v] = dep[u] + e[i].d; ring[u] = false;
dfs(v, i);
if (!ring[u]) R::addedge(u, v, e[i].d);
} else if (idx[v] < idx[u]) {
++nn; R::addedge(v, nn, 0); // 强行把环的根放在最前面
R::len[nn] = dep[u] - dep[v] + e[i].d;
for (int x = u; x != v; x = fa[x]) {
ring[x] = true;
int L = dep[x] - dep[v], R = dep[u] - dep[x] + e[i].d;
int d = min(L, R); R::lr[x] = d == L;
R::addedge(nn, x, d);
}
ring[v] = true;
}
}
}
}

int main() {
#ifdef zerol
freopen("in", "r", stdin);
#endif
X::init();
int m, Qn; cin >> n >> m >> Qn; nn = n;
FOR (_, 0, m) {
int u, v, d; scanf("%d%d%d", &u, &v, &d);
X::addedge(u, v, d);
}
X::dfs(1, -1);

R::init();
while (Qn--) {
int u, v; scanf("%d%d", &u, &v);
printf("%d\n", R::query(u, v));
}
}