题目大意:给一个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; }