# 【BZOJ2125】最短路

【题目链接】

【思路要点】

• 建立圆方树，预处理根节点到每个圆点的最短路的长度$$dist_i$$。
• 询问时分Lca为圆点和方点分别讨论。
• Lca为圆点，那么答案就是$$dist_x+dist_y-2*dist_{Lca}$$。
• Lca为方点，那么答案就是$$dist_x-dist_{tx}+dist_y-dist_{ty}+query(Lca,tx,ty)$$。
• 其中$$tx$$、$$ty$$分别为由$$Lca$$向$$x$$和$$y$$走一步所到达的节点，$$query(Lca,tx,ty)$$为$$tx$$与$$ty$$之间在环上的最短路。
• 时间复杂度$$O(NLogN+QLogN)$$。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 20005;
const int MAXLOG = 15;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); }
template <typename T> void read(T &x) {
x = 0; int f = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T> void write(T x) {
if (x < 0) x = -x, putchar('-');
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
write(x);
puts("");
}
struct edge {int dest, len; };
vector <edge> a[MAXN];
vector <int> b[MAXN];
map <int, int> mp[MAXN], c[MAXN];
int n, m, q, oldn;
int top, Stack[MAXN], len[MAXN];
int timer, dfn[MAXN], low[MAXN];
int father[MAXN][MAXLOG], depth[MAXN], dist[MAXN];
int query(int from, int x, int y) {
int tmp = abs(c[from][x] - c[from][y]);
return min(tmp, len[from] - tmp);
}
void work(int pos, int fa) {
father[pos][0] = fa;
depth[pos] = depth[fa] + 1;
for (int i = 1; i < MAXLOG; i++)
father[pos][i] = father[father[pos][i - 1]][i - 1];
if (pos <= oldn) {
int f = father[fa][0];
if (pos == 1) dist[pos] = 0;
else dist[pos] = dist[f] + query(fa, pos, f);
for (unsigned i = 0; i < b[pos].size(); i++)
if (b[pos][i] != fa) work(b[pos][i], pos);
} else {
for (unsigned i = 0; i < b[pos].size(); i++)
if (b[pos][i] != fa) work(b[pos][i], pos);
}
}
void tarjan(int pos) {
Stack[++top] = pos;
dfn[pos] = low[pos] = ++timer;
for (unsigned i = 0; i < a[pos].size(); i++)
if (dfn[a[pos][i].dest] == 0) {
tarjan(a[pos][i].dest);
chkmin(low[pos], low[a[pos][i].dest]);
if (low[a[pos][i].dest] >= dfn[pos]) {
int tmp = 0, last = 0; n++;
while (tmp != a[pos][i].dest) {
last = tmp;
tmp = Stack[top--];
b[n].push_back(tmp);
b[tmp].push_back(n);
if (last == 0) len[n] += mp[tmp][pos];
else len[n] += mp[tmp][last];
c[n][tmp] = len[n];
}
b[n].push_back(pos);
b[pos].push_back(n);
len[n] += mp[pos][tmp];
c[n][pos] = len[n];
}
} else chkmin(low[pos], dfn[a[pos][i].dest]);
}
int lca(int x, int y) {
if (depth[x] < depth[y]) swap(x, y);
for (int i = MAXLOG - 1; i >= 0; i--)
if (depth[father[x][i]] >= depth[y]) x = father[x][i];
if (x == y) return x;
for (int i = MAXLOG - 1; i >= 0; i--)
if (father[x][i] != father[y][i]) {
x = father[x][i];
y = father[y][i];
}
return father[x][0];
}
int getans(int x, int y, int z) {
int ans = dist[x] + dist[y];
for (int i = MAXLOG - 1; i >= 0; i--) {
if (depth[father[x][i]] > depth[z]) x = father[x][i];
if (depth[father[y][i]] > depth[z]) y = father[y][i];
}
ans -= dist[x] + dist[y];
ans += query(z, x, y);
return ans;
}
int main() {
for (int i = 1; i <= m; i++) {
int x, y, z;
if (mp[x].count(y)) chkmin(mp[x][y], z);
else mp[x][y] = z;
if (mp[y].count(x)) chkmin(mp[y][x], z);
else mp[y][x] = z;
a[x].push_back((edge) {y, z});
a[y].push_back((edge) {x, z});
}
tarjan(1);
work(1, 0);
for (int i = 1; i <= q; i++) {
int x, y;
int z = lca(x, y);
if (z <= oldn) writeln(dist[x] + dist[y] - 2 * dist[z]);
else writeln(getans(x, y, z));
}
return 0;
}

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客