货车运输
原题链接 货车运输
题目大意
有 (n) 个点,然后给你 (m) 条边,每条边我们有边权 (k) ,现在给你 (q) 次询问,每次询问两个整数 (x, y),表示从 (x) 到 (y) 的最大值为多少,这个值就是走过的边权的最小值。
题目题解
这道题很明显可以得到的是,一些边我们从始至终都不会走的,因为有别的更优的路径,这让我们想到了最大生成树了,那求出最大生成树之后怎么办呢?我们发现,对于两个点而言,在最大生成树中有且仅有一条路径可以互通,如何让电脑直接走这条路呢,嗯?是不是很熟悉,对,就是LCA,求最近公共祖先,但光只有LCA肯定是不行的,因为我们这次带上了边权,其实问题很简单,根据题目大意我们得到:我们的问题是找到我们走的边的最小边权,那么就意味着我们可以维护一个与记录(x)点的(2^k)的数组一样的数组,维护(x)点走(2^k)后所能得到的最小值,那么这样我们就能得到我们的答案了
代码如下
//#define fre yes
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define INF 9999999
const int N = 50005;
struct Node {
int x, y, z;
}g[N];
int head[N], to[N], ver[N], edge[N];
int par[N];
int depth[N], f[N][22], w[N][22], lg[N];
bool Vis[N];
int n;
void init() {
for (int i = 1; i <= n; i++) {
par[i] = i;
}
}
int tot;
void addedge(int x, int y, int z) {
ver[tot] = y;
edge[tot] = z;
to[tot] = head[x];
head[x] = tot++;
}
bool cmp(Node x, Node y) {
return x.z > y.z;
}
int find(int x) {
if(x == par[x]) return par[x];
else return par[x] = find(par[x]);
}
void dfs(int x, int fa) {
Vis[x] = 1;
depth[x] = depth[fa] + 1;
f[x][0] = fa;
for (int i = 1; (1 << i) <= depth[x]; i++) {
f[x][i] = f[f[x][i - 1]][i - 1];
w[x][i] = std::min(w[x][i - 1], w[f[x][i - 1]][i - 1]);
}
for (int i = head[x]; ~i; i = to[i]) {
int v = ver[i];
if(v != fa) {
w[v][0] = edge[i];
dfs(v, x);
}
}
}
int lca(int x, int y) {
if(find(x) != find(y)) return -1;
int ans = INF;
if(depth[x] < depth[y]) {
std::swap(x, y);
}
while(depth[x] > depth[y]) {
ans = std::min(ans, w[x][lg[depth[x] - depth[y]] - 1]);
x = f[x][lg[depth[x] - depth[y]] - 1];
}
if(x == y) return ans;
for (int i = lg[depth[x]] - 1; i >= 0; i--) {
if(f[x][i] != f[y][i]) {
ans = std::min(ans, std::min(w[x][i], w[y][i]));
x = f[x][i];
y = f[y][i];
}
}
ans = std::min(ans, std::min(w[x][0], w[y][0]));
return ans;
}
int main() {
memset(head, -1, sizeof(head));
memset(w, 0x3f, sizeof(w));
static int m;
scanf("%d %d", &n, &m);
init();
for (int i = 1; i <= m; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
g[i].x = x;
g[i].y = y;
g[i].z = z;
}
std::sort(g + 1, g + 1 + m, cmp);
for (int i = 1; i <= m; i++) {
int x = find(g[i].x);
int y = find(g[i].y);
if(x == y) continue;
par[x] = y;
addedge(g[i].x, g[i].y, g[i].z);
addedge(g[i].y, g[i].x, g[i].z);
}
for (int i = 1; i <= n; i++) {
if(!Vis[i]) {
dfs(i, 0);
}
}
for (int i = 1; i <= n; i++) {
lg[i] = lg[i - 1] + (1 << lg[i - 1] == i);
}
static int t;
scanf("%d", &t);
for (int i = 1; i <= t; i++) {
int x, y;
scanf("%d %d", &x, &y);
printf("%d
", lca(x, y));
} return 0;
}