洛谷 P1967 [NOIP 2013 提高组] 货车运输(kruskal 重构树 + 求路径最小边权)
题目链接
题目难度
洛谷上是蓝题,本人认为评高了,此题思维和实现都不难,应该是绿题。
题目解法概括
kruskal 重构树 + 倍增优化求路径最小边权
代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;typedef long long LL;const LL Maxn = 1e4 + 5;
const LL Maxm = 5 * 1e4 + 5;
const LL Maxk = 20;
const LL Inf = 1e18;struct node {LL x, y, z;
} Edge[Maxm];struct node2 {LL to, e;
};
vector<node2> tree[Maxn];LL bfa[Maxn], father[Maxn][Maxk], depth[Maxn], minDis[Maxn][Maxk];
bool vis[Maxn];void init_bfa(LL n) {for (LL i = 0; i <= n; ++i) bfa[i] = i;return;
}LL f_find(LL x) {if (x == bfa[x]) return x;else return bfa[x] = f_find(bfa[x]);
}bool f_join(LL u, LL v) {u = f_find(u);v = f_find(v);if (u == v) return false;bfa[u] = v;return true;
}void dfs(LL u, LL fa) {vis[u] = true;father[u][0] = fa;depth[u] = depth[fa] + 1;for (LL i = 1; depth[u] > (1 << i); ++i) {father[u][i] = father[father[u][i - 1]][i - 1];minDis[u][i] = min(minDis[u][i - 1], minDis[father[u][i - 1]][i - 1]);}for (auto elem : tree[u]) {if (elem.to == fa) continue;minDis[elem.to][0] = elem.e;dfs(elem.to, u);}return;
}LL path_num(LL u, LL v) {if (depth[u] < depth[v]) swap(u, v);LL res = Inf;for (LL i = Maxk - 1; i >= 0; --i) {if (depth[father[u][i]] >= depth[v]) {res = min(res, minDis[u][i]);u = father[u][i];}}if (u == v) return res;for (LL i = Maxk - 1; i >= 0; --i) {if (father[u][i] != father[v][i]) {res = min(res, min(minDis[u][i], minDis[v][i]));u = father[u][i];v = father[v][i];}}if (father[u][0] == 0) return -1;else {res = min(res, min(minDis[u][0], minDis[v][0]));return res;}
}int main() {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);LL n, m, q, a, b;cin >> n >> m;for (LL i = 1; i <= m; ++i) {cin >> Edge[i].x >> Edge[i].y >> Edge[i].z;}sort(Edge + 1, Edge + m + 1, [](const node& a, const node& b) {return a.z > b.z;});init_bfa(n);for (LL i = 1; i <= m; ++i) {if (f_join(Edge[i].x, Edge[i].y) != false) {tree[Edge[i].x].push_back({Edge[i].y, Edge[i].z});tree[Edge[i].y].push_back({Edge[i].x, Edge[i].z});}}for (LL i = 1; i <= n; ++i) {if (vis[i] == false) dfs(i, 0);}cin >> q;while (q--) {cin >> a >> b;cout << path_num(a, b) << '\n';}return 0;
}
总结
只考虑和结果有关的部分,是此题的突破点,也是重要的思维方式。
解法
若 x 到 y 有多条路径,只需考虑最小边权最大的路径,直接找这样的路径,比较费时,可以考虑直接去掉无用的路径,那么用 kruskal 重构树构建最大瓶颈树。我们只考虑和结果有关的部分。
此时,问题就变成了树上的求路径的最小边权问题,需要求 LCA,求 LCA 和求路径的最小边权可以同步进行。