D - Shortest Path Query

题意:

给定一个图,图中每一条边的两点符合以下条件:两点编号二进制表示下一个位另一个的前缀。给出q次询问,每次给出u,v,询问u和v之间的最短路。

解析:

字典树符合一个点的为其所有子节点的前缀,故使用字典树对所有点建树,并将原图中的边附加上去,得到一颗有附加边的完全二叉树。对于该图中的任一节点,一个点无法不经过该点从左子树走到右子树,故对于询问的u,v,只需求u和v的所有公共祖先,并对所有公共祖先求min(dis[Lca][u]+dis[Lca][v])min(dis[Lca][u]+dis[Lca][v])即可。对每个公共祖先求最短路可以在用到的时候再求,可以提高速度。

代码:
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
#include <bits/stdc++.h>

using namespace std;
#define fast \
ios_base::sync_with_stdio(false); \
cin.tie(NULL);
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const long long LINF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-3;
int tree[M][2];
int idx = 0;
struct node{
int u,v,next;
long long w;
}a[M];
int f[N],cnt;
void add(int u,int v,int w){
a[++cnt].v = v;
a[cnt].w = w;
a[cnt].next = f[u];
f[u] = cnt;
}
int n, m;
int father[N][30], dep[N];
void build(int u, int fa, int deep){
if(u > n) return;
father[u][0] = fa;
dep[u] = deep;
build(u << 1, u, deep + 1);
build(u << 1 | 1, u, deep + 1);
}
int lg = 0;
int lca(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
for (int i = lg; i >= 0; i--) {
if(dep[father[x][i]] >= dep[y]) x = father[x][i];
}
if(x == y) return x;
for (int i = lg; i >= 0; i--) {
if(father[x][i] != father[y][i]){
x = father[x][i];
y = father[y][i];
}
}
return father[x][0];
}
unordered_map<int, unordered_map<int, long long>> dis;
unordered_map<int, unordered_map<int, bool>> vis;
void dijkstra(int s) {
priority_queue<pair<long long, int>> q;
q.push({0, s});
dis[s][s] = 0;
while (!q.empty()) {
int u = (q.top()).second;
q.pop();
if(vis[s][u]) continue;
vis[s][u] = 1;
for (int i = f[u]; i; i = a[i].next) {
int v = a[i].v;
if (dep[v] <= dep[s]) continue;
long long w = a[i].w;
if (dis[s].count(v) == 0 || dis[s][v] > dis[s][u] + w) {
dis[s][v] = dis[s][u] + w;
q.push({-dis[s][v], v});
}
}
}
}
int main(){
cin >> n >> m;
build(1, 0, 1);
lg = (int)log(n) / log(2) + 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= lg; j++) {
father[i][j] = father[father[i][j - 1]][j - 1];
}
}
int u, v, w;
for (int i = 1; i <= m; i++){
scanf("%d%d%d", &u, &v, &w);
add(v, u, w);
add(u, v, w);
}
int q;
cin >> q;
while(q--) {
int u, v;
scanf("%d%d", &u, &v);
int Lca = lca(u, v);
long long Min = LINF;
while(Lca) {
if(dis[Lca].count(Lca) == 0) dijkstra(Lca);
if(dis[Lca].count(u) && dis[Lca].count(v)) Min = min(Min, dis[Lca][u] + dis[Lca][v]);
Lca = father[Lca][0];
}
if(Min == LINF) printf("-1\n");
else printf("%lld\n", Min);
}
return 0;
}