用 kruskal+lca 写的,谁有hack但不想调的直接发过来。
里头有注释:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double dbl;
const int maxn = 1e4+5, maxm = 5e4+5;
typedef array<int, 3> edge; //三元组
int n, m, Q;
edge e[maxm]; //旧图
list<array<int, 2>> g[maxn]; //新图(kruskal后)
int ds[maxn]; //并查集
int dep[maxn], dp[maxn][20], w[maxn][20];
//dep=深度 dp=倍增数组
//w[i][j]=i~(i+2^j)之间最小边权
bool vis[maxn];
int seek(int x){
return x == ds[x] ? x : ds[x] = seek(ds[x]);
}
void init(int v, int fa, int fw){
//fa是上一个节点,fw是v与fa之间的边权
if (~fa) dep[v] = dep[fa]+1;
dp[v][0] = fa; w[v][0] = fw;
vis[v] = 1;
for (int i = 1; i <= 20; i++){
if (~dp[v][i-1]){
dp[v][i] = dp[dp[v][i-1]][i-1];
w[v][i] = min(w[v][i-1], w[dp[v][i-1]][i-1]);
}else dp[v][i] = -1;
} //预处理倍增
for (auto t : g[v]){
if (t[0] == fa) continue;
init(t[0], v, t[1]);
} //继续遍历
}
int lca(int x, int y){
if (seek(x) != seek(y)) return -1; //特判连通
int ret = INT_MAX;
if (dep[x] > dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--){
if (dp[y][i] == -1) continue;
if (dep[dp[y][i]] >= dep[x]){
ret = min(ret, w[y][i]); y = dp[y][i];
}
} //使x与y同深度
if (x == y) return ret;
for (int i = 20; i >= 0; i--){
if (dp[x][i] == -1 || dp[y][i] == -1) continue;
if (dp[x][i] != dp[y][i]){
x = dp[x][i]; y = dp[y][i];
ret = min({ret, w[x][i], w[y][i]});
}
} //找到lca,并求x~lca和y~lca中的最小权值
return ret + w[x][0];
}
int main(){
scanf("%d %d", &n, &m);
for (int i = 0; i < m; i++){
scanf("%d %d %d", &e[i][0], &e[i][1], &e[i][2]);
e[i][0]--; e[i][1]--;
}
sort(e, e+m, [](edge l, edge r){return l[2] > r[2];});
iota(ds, ds+n, 0);
for (int i = 0; i < m; i++){
int f1 = seek(e[i][0]), f2 = seek(e[i][1]);
if (f1 == f2) continue;
ds[f1] = f2;
g[e[i][0]].push_back({e[i][1], e[i][2]});
g[e[i][1]].push_back({e[i][0], e[i][2]});
} //kruskal
for (int i = 0; i < n; i++) if (!vis[i]) init(i, -1, INT_MAX);
scanf("%d", &Q); while (Q--){
int x, y; scanf("%d %d", &x, &y);
if (x > n || y > n){
puts("-1");
continue;
}
x--; y--;
printf("%d\n", lca(x, y));
}
return 0;
}