解法:首先想到肯定是尽量用边权大的边,所以直接用最大生成树建图即可。建完图之后,由于询问数很大,所以要想一个高效的求两点之间边权最小的边的办法。一开始想到树链剖分,感觉很麻烦,代码量巨大。想用LCA的ST表来维护,但是发现维护困难,因为是边而不是点。最后才知道要用树上倍增做。预处理出深度小的点到深度大的i点最小的边权是多少。先求出x和y的lca,然后直接求出x到lca, y到lca的最小边权。取最小值即可。
代码如下:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 5e5 + 5;
const int M = 25; //树的深度
int dp[2 * maxn][M]; //这个数组记得开到2*maxn,因为遍历后序列长度为2*n-1
bool vis[maxn];
struct edge {
int u, v, w, next;
} e[2 * maxn];
int tot, head[maxn];
inline void add(int u ,int v ,int w ,int &k) {
e[k].u = u; e[k].v = v; e[k].w = w;
e[k].next = head[u]; head[u] = k++;
u = u ^ v; v = u ^ v; u = u ^ v;
e[k].u = u; e[k].v = v; e[k].w = w;
e[k].next = head[u]; head[u] = k++;
}
int dir[maxn], d[maxn][M];
int fa[maxn][M];
void dfs(int u ,int dep) {
vis[u] = true;
for(int i = 1; i < M; i++) {
if(dir[u] < (1 << i))
break;
fa[u][i] = fa[fa[u][i - 1]][i - 1];
d[u][i] = min(d[u][i - 1], d[fa[u][i - 1]][i - 1]);
}
for(int k = head[u]; ~k; k = e[k].next)
if( !vis[e[k].v] ) {
int v = e[k].v , w = e[k].w;
// cout << v << ' ' << w << '\n';
dir[v] = dir[u] + 1;
fa[v][0] = u;
d[v][0] = w;
dfs(v, dep + 1);
}
}
int LCA(int x, int y) {
if(dir[x] < dir[y])
swap(x, y);
int t = dir[x] - dir[y];
for(int i = 0; i < M; i++)
if((1 << i) & t)
x = fa[x][i];
for(int i = M - 1; i >= 0; i--) {
if(fa[x][i] != fa[y][i]) {
x = fa[x][i];
y = fa[y][i];
}
}
if(x == y) //如果y就是x的最近公共祖先/x本来就等于y
return x;
return fa[x][0];
}
int ask(int x, int y) {
int ans = 0x3f3f3f3f;
int t = dir[x] - dir[y];
for(int i = 0; i < M; i++) {
if((1 << i) & t) {
ans = min(ans, d[x][i]);
x = fa[x][i];
}
}
return ans;
}
int n, m, par[maxn];
struct Node {
int u, v, val;
bool operator < (const Node& t) const {
if(val > t.val)
return 1;
return 0;
}
}edge[maxn];
int Find(int x) {
int tmp = x;
while(x != par[x])
x = par[x];
int root = x;
x = tmp;
while(x != par[x]) {
tmp = par[x];
par[x] = root;
x = tmp;
}
return root;
}
void Kruskal(int &num) {
for(int i = 1; i <= n; i++)
par[i] = i;
for(int i = 0; i < m; i++) {
int x = Find(edge[i].u);
int y = Find(edge[i].v);
if(x != y) {
par[y] = x;
add(edge[i].u, edge[i].v, edge[i].val, num);
}
}
}
int main() {
//#ifndef ONLINE_JUDGE
// freopen("in.txt","r",stdin);
//#endif
memset(head, -1, sizeof(head));
memset(vis, false, sizeof(vis));
int num = 0, q, x, y;;
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++)
scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].val);
sort(edge, edge + m);
Kruskal(num);
tot = 0;
dir[Find(1)] = 0;
dfs(par[1], 1);
scanf("%d", &q);
while(q--) {
scanf("%d%d", &x, &y);
if(Find(x) != Find(y)) {
printf("-1\n");
continue;
}
int lca = LCA(x, y);
printf("%d\n", min(ask(x, lca), ask(y, lca)));
}
return 0;
}