Loading

【题解】Luogu[P1967] NOIP2013 提高组 货车运输

Link→

很容易想到一个暴力做法,就是跑一遍 Floyd,\(F_{i,j}\) 表示 \(i\)\(j\) 最大载重量,转移 \(F_{i,j}=\max\{F_{i, j}, \min\{F_{i, k}, F_{k,j}\}\}\)。显然时间复杂度 \(O(n^3)\) 是过不了的。

我们发现,因为是求两点路径中使得最小值最大,实际上有一些较小的路径是不会走到的,只是需要走那些值较大的路径。于是我们可以考虑将其删掉,只保留那些较大的路线,于是考虑建最大生成树。

建好树后,考虑对于每次询问如何处理,我们发现树上两点的路径是唯一的,对于两点,我们可以通过求 LCA 计算答案,两点树上倍增求 LCA,过程中计算答案即可。

#include <bits/stdc++.h>
using namespace std;
const int _ = 1e4 + 5, __ = 20, inf = 1e9;
int n, m, q;
struct node {
  int to/*x*/, nxt/*y*/, w/*dis*/;
} e1[5 * _], e2[5 * _]; int cnt, head[_];
// e1原始图,e2最大生成树
void add2(int u, int v, int w) { // 链式前向星存图
  e2[++cnt] = {v, head[u], w};
  head[u] = cnt;
}
bool cmp(node x, node y) { return x.w > y.w; }
int f[_];
int getfa(int x) { // 并查集
  if(f[x] == x) return x;
  return f[x] = getfa(f[x]);
}
void kruskal() { // kruskal 求最大生成树
  sort(e1 + 1, e1 + m + 1, cmp);
  for(int i = 1; i <= n; ++i) f[i] = i;
  for(int i = 1; i <= m; ++i)
    if(getfa(e1[i].to) != getfa(e1[i].nxt))
      f[getfa(e1[i].to)] = getfa(e1[i].nxt),
      add2(e1[i].to, e1[i].nxt, e1[i].w), add2(e1[i].nxt, e1[i].to, e1[i].w); // 建树
}
int fa[_][__ + 5], dis[_][__ + 5], dep[_]; bool vis[_];
// dis 树上倍增求值
void init() { // LCA 预处理
  for(int k = 1; k <= __; ++k)
    for(int i = 1; i <= n; ++i)
      fa[i][k] = fa[fa[i][k - 1]][k - 1],
      dis[i][k] = min(dis[i][k - 1], dis[fa[i][k - 1]][k - 1]);
}
void dfs(int u) {
  vis[u] = true;
  for(int i = head[u]; i; i = e2[i].nxt) {
    int v = e2[i].to;
    if(!vis[v]) fa[v][0] = u, dis[v][0] = e2[i].w, dep[v] = dep[u] + 1, dfs(v);
  }
}
int lca(int x, int y) { // 倍增求 LCA
  int res = inf;
  if(getfa(x) != getfa(y)) return -1; // 如果两点不连通
  if(dep[x] > dep[y]) swap(x, y);
  for(int i = __; i >= 0; --i)
    if(dep[fa[y][i]] >= dep[x])
      res = min(res, dis[y][i]), y = fa[y][i]; // 过程中维护答案
  if(x == y) return res;
  for(int i = __; i >= 0; --i)
    if(fa[x][i] != fa[y][i])
      res = min(res, min(dis[x][i], dis[y][i])),
      x = fa[x][i], y = fa[y][i];
  res = min(res, min(dis[x][0], dis[y][0])); // 处理 LCA 处答案
  return res;
}
void solve() {
  int u, v; cin >> u >> v;  
  cout << lca(u, v) << endl;
}
int main() {
  cin >> n >> m;
  for(int i = 1, u, v, w; i <= m; ++i)
    cin >> u >> v >> w, e1[i] = {u, v, w};
  kruskal();
  for(int i = 1; i <= n; ++i)
    if(!vis[i])
      dep[i] = 1, dfs(i), fa[i][0] = i, dis[i][0] = inf;
  init();
  cin >> q; while(q--) solve();
  return 0;
}
posted @ 2023-05-13 22:17  AgrumeStly  阅读(37)  评论(0)    收藏  举报