【NOIP2013提高组】货车运输
https://www.luogu.org/problem/show?pid=1967
思考一下,将图的所有边按边权从大到小依次加入图,则当u与v第一次连通时,刚加入的边就是使u与v两点的路径中的最小边最大的边。
将图的所有边按边权从大到小依次加入图?这不就是Kruscal算法最大生成树吗!
所以我们只需要对原图求最大生成树,对于每个询问求两点的路径上的最小边就可以了。虽然可以用树剖+ST表优化,但是暴力求LCA+暴力爬链也能过了。
暴力LCA+暴力爬链:
#include <algorithm> #include <cmath> #include <iostream> #include <vector> #define maxn 10005 #define maxq 30005 using namespace std; int n, m; // 树 struct edge { int from, to, weight; }; bool cmp(const edge& x, const edge& y) { return x.weight > y.weight; } vector<edge> edges; vector<int> tree[maxn]; void addedge(int u, int v, int w) { edges.push_back((edge){u, v, w}); tree[u].push_back(edges.size() - 1); edges.push_back((edge){v, u, w}); tree[v].push_back(edges.size() - 1); } bool visited[maxn]; int depth[maxn], parent[maxn]; int weight[maxn]; void buildtree(int v, int fr, int d) { depth[v] = d; parent[v] = fr; visited[v] = true; for (int i = 0; i < tree[v].size(); i++) { int w = edges[tree[v][i]].to; if (w != fr) { weight[w] = edges[tree[v][i]].weight; buildtree(w, v, d + 1); } } } int lca(int x, int y) { while (x != y) { if (depth[x] < depth[y]) swap(x, y); x = parent[x]; } return x; } int minedge(int v, int w) { int x = lca(v, w), ans = 0x7fffffff; while (v != x) { ans = min(ans, weight[v]); v = parent[v]; } while (w != x) { ans = min(ans, weight[w]); w = parent[w]; } return ans; } // 并查集与Kruscal namespace djs { int parent[maxn]; void init() { for (int i = 1; i <= n; i++) parent[i] = -1; } int find(int x) { if (parent[x] < 0) return x; else return parent[x] = find(parent[x]); } void merge(int x, int y) { x = find(x); y = find(y); if (x == y) return; if (parent[x] > parent[y]) swap(x, y); parent[x] += parent[y]; parent[y] = x; } bool related(int x, int y) { return find(x) == find(y); } } vector<edge> e; void kruscal() { sort(e.begin(), e.end(), cmp); djs::init(); int cnt = 0; for (int i = 0; i < e.size(); i++) { if (!djs::related(e[i].from, e[i].to)) { djs::merge(e[i].from, e[i].to); addedge(e[i].from, e[i].to, e[i].weight); cnt++; } } } int main() { ios::sync_with_stdio(false); cin >> n >> m; int a, b, c; for (int i = 1; i <= m; i++) { cin >> a >> b >> c; e.push_back((edge){a, b, c}); } kruscal(); for (int i = 1; i <= n; i++) { if (!visited[i]) buildtree(i, 0, 1); } int nq; cin >> nq; for (int i = 1; i <= nq; i++) { cin >> a >> b; if (djs::related(a, b)) cout << minedge(a, b) << endl; else cout << -1 << endl; } return 0; }
树剖+ST表优化:
#include <algorithm> #include <cmath> #include <iostream> #include <vector> #define maxn 10005 #define maxq 30005 using namespace std; const int inf = 0x7fffffff; int n, m; // ST表 namespace st { int logn[maxn]; int dp[maxn][15]; int val[maxn]; void init() { for (int i = 1; i <= n; i++) { dp[i][0] = val[i]; logn[i] = log(i) / log(2); } // f(i,j)=min{f(i,j-1),f(i+2^(j-1),j-1)} int limit = logn[n] + 1; for (int j = 1; j <= limit; j++) { for (int i = 1; i <= n; i++) { if (i + (1 << (j - 1)) <= n) dp[i][j] = min(dp[i][j - 1], dp[i + (1 << (j - 1))][j - 1]); else dp[i][j] = dp[i][j - 1]; } } } int min(int l, int r) { if (l > r) return 0x7fffffff; // return max{f(l,k),f(r-2^k+1,k)} int k = logn[r - l + 1]; return std::min(dp[l][k], dp[r - (1 << k) + 1][k]); } } // 树与树剖 struct edge { int from, to, weight; }; bool cmp(const edge &x, const edge &y) { return x.weight > y.weight; } vector<edge> edges; vector<int> tree[maxn]; void addedge(int u, int v, int w) { edges.push_back((edge){u, v, w}); tree[u].push_back(edges.size() - 1); edges.push_back((edge){v, u, w}); tree[v].push_back(edges.size() - 1); } bool visited[maxn]; int size[maxn], depth[maxn], parent[maxn], heavy[maxn]; int weight[maxn]; void buildtree(int v, int fr, int d) { depth[v] = d; parent[v] = fr; size[v] = 1; heavy[v] = 0; visited[v] = true; for (int i = 0; i < tree[v].size(); i++) { int w = edges[tree[v][i]].to; if (w != fr) { weight[w] = edges[tree[v][i]].weight; buildtree(w, v, d + 1); size[v] += size[w]; if (size[heavy[v]] < size[w]) heavy[v] = w; } } } int timer = 1; int top[maxn], hashh[maxn]; void slpf(int v, int tp) { st::val[timer] = weight[v]; hashh[v] = timer++; top[v] = tp; if (heavy[v]) { slpf(heavy[v], tp); for (int i = 0; i < tree[v].size(); i++) { int w = edges[tree[v][i]].to; if (w != parent[v] && w != heavy[v]) slpf(w, w); } } } int minedge(int v, int w) { int ans = 0x7fffffff; int vp, wp; while (true) { vp = top[v]; wp = top[w]; if (vp == wp) break; if (depth[vp] < depth[wp]) { swap(v, w); swap(vp, wp); } ans = min(ans, st::min(hashh[vp], hashh[v])); v = parent[vp]; } if (depth[v] > depth[w]) swap(v, w); ans = min(ans, st::min(hashh[v] + 1, hashh[w])); return ans; } // 并查集与Kruscal namespace djs { int parent[maxn]; void init() { for (int i = 1; i <= n; i++) parent[i] = -1; } int find(int x) { if (parent[x] < 0) return x; else return parent[x] = find(parent[x]); } void merge(int x, int y) { x = find(x); y = find(y); if (x == y) return; if (parent[x] > parent[y]) swap(x, y); parent[x] += parent[y]; parent[y] = x; } bool related(int x, int y) { return find(x) == find(y); } } vector<edge> e; void kruscal() { sort(e.begin(), e.end(), cmp); djs::init(); int cnt = 0; for (int i = 0; i < e.size(); i++) { if (!djs::related(e[i].from, e[i].to)) { djs::merge(e[i].from, e[i].to); addedge(e[i].from, e[i].to, e[i].weight); cnt++; } } } int main() { ios::sync_with_stdio(false); cin >> n >> m; int a, b, c; for (int i = 1; i <= m; i++) { cin >> a >> b >> c; e.push_back((edge){a, b, c}); } kruscal(); for (int i = 1; i <= n; i++) { if (!visited[i]) { buildtree(i, 0, 1); slpf(i, i); } } st::init(); int nq; cin >> nq; for (int i = 1; i <= nq; i++) { cin >> a >> b; if (djs::related(a, b)) cout << minedge(a, b) << endl; else cout << -1 << endl; } return 0; }