题目大意:给一个n(0 < n < 10000)个点,m(0 < m < 50000)条边的带权双向图,给q(0 < q < 30000)个询问,每次询问两个点x, y,输出x到y所有路径上最小边的最大值。
分析:
因为要求所有路径上最小边的最大值,所以先求一遍最大生成树,通过最大生成树的边求得的路径一定是最大的,求的过程中用邻接表建图,之后对于每个询问x, y,用st-lca求x, y的最近公共祖先(LCA),求的过程中对经过的路程取min.
那么,这里介绍一下st-lca算法:
用dep[i]表示结点i的深度,f[i][j]表示结点i的2^j祖先,minv[i][j]表示结点i到其2^j祖先路径上的最小值。
第一步,dfs,得到所有点的dep值,初始化f和minv.
第二步,得到所有的f和minv,其中f[i][j] = f[f[i][j-1]][j-1],minv[i][j] = min(minv[i][j-1], minv[f[i][j-1]][j-1]).
第三步,开始正式lca过程:
1.将y调整为深度比较大的。
2.将询问的两个点调整至相同深度。
3.将两个点调整至lca的儿子。
4.最后返回答案的时候,如果lca是0,那么表示无解,否则最后答案要注意ans = min(ans, min(minv[x][0], minv[y][0])).
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n, m, q, x, y, cnt, hd[10005], nxt[20005], to[20005], w[20005], p[50005], f[10005][20], minv[10005][20], dep[10005];
struct edge {
int x, y, z;
bool operator < (const edge &rhs) const {
return z > rhs.z;
}
}e[50005];
void add(int x, int y, int z) {
to[cnt] = y;
w[cnt] = z;
nxt[cnt] = hd[x];
hd[x] = cnt++;
}
int fnd(int x) {
return p[x] == x ? x : p[x] = fnd(p[x]);
}
void kruskal() {
for(int i = 0; i < m; i++) p[i] = i;
sort(e, e+m);
for(int i = 0; i < m; i++) if(fnd(e[i].x) != fnd(e[i].y)) {
add(e[i].x, e[i].y, e[i].z);
add(e[i].y, e[i].x, e[i].z);
p[fnd(e[i].x)] = fnd(e[i].y);
}
}
void dfs(int x, int p) {
for(int i = hd[x]; ~i; i = nxt[i]) if(i != p) {
dep[to[i]] = dep[x] + 1;
f[to[i]][0] = x;
minv[to[i]][0] = w[i];
dfs(to[i], i ^ 1);
}
}
int lca(int x, int y) {
int ans = 100000000;
if(dep[x] > dep[y]) swap(x, y);
for(int i = 15; i >= 0; i--) if(dep[f[y][i]] >= dep[x]) {
ans = min(ans, minv[y][i]);
y = f[y][i];
}
if(x == y) return ans;
for(int i = 15; i >= 0; i--) if(f[x][i] != f[y][i]) {
ans = min(ans, min(minv[x][i], minv[y][i]));
x = f[x][i];
y = f[y][i];
}
return f[x][0] == 0 ? -1 : min(ans, min(minv[x][0], minv[y][0]));
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 0; i < m; i++) scanf("%d%d%d", &e[i].x, &e[i].y, &e[i].z);
memset(hd, -1, sizeof hd);
kruskal();
dep[1] = 1;
dfs(1, -1);
for(int j = 1; j <= 15; j++)
for(int i = 1; i <= n; i++) {
f[i][j] = f[f[i][j-1]][j-1];
minv[i][j] = min(minv[i][j-1], minv[f[i][j-1]][j-1]);
}
scanf("%d", &q);
while(q--) scanf("%d%d", &x, &y), printf("%d\n", lca(x, y));
return 0;
}