NOIP2015 提高组 运输计划

LCA,倍增,树上差分

算法 1

对于 \(m=1\) 的数据。

答案就是路径和减去路径上的最大权值。一次 DFS 即可。

算法 2

对于链的情况。

一个序列问题,二分枚举 \(t\),那么我们需要将所有 \(> t\) 的线段变成 \(\leq t\) 的。我们先求出所有 \(> t\) 的线段的交集,如果这些线段没有交集,则无解。然后我们删除其中最大的那个,如果仍无法让最大的线段 \(\leq t\),则无解。

算法 3

对于 \(100\%\) 的数据。

与算法 2 实际上是相同的,我们可以在求 LCA 的过程中求出路径长度,但是如何求路径的交呢?考虑树上差分。假设一条路径 \(u \to v\),我们在 \(u \to v\) 的路径的电上都加上 \(1\),最后哪条边的边权是 \(> t\) 的线段的个数,这条边就是这些路径的交集。对于实现这个操作,我们可以在 \(u,v\) 的地方加上 \(1\),在 \(\operatorname{LCA}(u,v)\) 处减去 \(2\),这样我们回溯时更新,\(u,v\) 的路径都加上了 \(1\),从 \(\operatorname{LCA}(u,v)\) 到其父亲的边不在路径上,更新时会加上两次 \(1\),所以减去 \(2\)

求路径长度时间复杂度为 \(O(m \log n)\),求解部分时间复杂度为 \(O(n \log w)\)

#include <bits/stdc++.h>

const int N = 300005;

int n, m, c[N], maxt;
std::vector<std::pair<int, int>> g[N];
struct Segment {
  int u, v, lca, dis;
} seg[N];

struct LCA {
  int f[N][20], dis[N][20], depth[N];
  void init(int u, int father) {
    f[u][0] = father;
    depth[u] = depth[father] + 1;
    for (int i = 1; i <= std::__lg(depth[u]); i++) {
      f[u][i] = f[f[u][i - 1]][i - 1];
      dis[u][i] = dis[u][i - 1] + dis[f[u][i - 1]][i - 1];
    }
    for (auto [v, w] : g[u]) {
      if (v == father) {
        continue;
      }
      dis[v][0] = w;
      init(v, u);
    }
  }
  std::pair<int, int> operator()(int u, int v) {
    int ans = 0;
    if (depth[u] < depth[v]) {
      std::swap(u, v);
    }
    while (depth[u] > depth[v]) {
      int jump = std::__lg(depth[u] - depth[v]);
      ans += dis[u][jump];
      u = f[u][jump];
    }
    if (u == v) {
      return {u, ans};
    }
    for (int i = std::__lg(depth[u]); i >= 0; i--) {
      if (f[u][i] != f[v][i]) {
        ans += dis[u][i] + dis[v][i];
        u = f[u][i], v = f[v][i];
      }
    }
    ans += dis[u][0] + dis[v][0];
    return {f[u][0], ans};
  }
} lca;

void dfs(int u, int father) {
  for (auto [v, w] : g[u]) {
    if (v == father) {
      continue;
    }
    dfs(v, u);
    c[u] += c[v];
  }
}

bool check(int t) {
  memset(c, 0, sizeof(c));
  int cnt = 0, res = 0;
  for (int i = 1; i <= m; i++) {
    if (seg[i].dis <= t) {
      continue;
    }
    c[seg[i].u]++;
    c[seg[i].v]++;
    c[seg[i].lca] -= 2;
    cnt++;
  }
  dfs(1, 0);
  for (int i = 1; i <= n; i++) {
    if (c[i] == cnt) {
      res = std::max(res, lca.dis[i][0]);
    }
  }
  return res >= maxt - t;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m;
  int l = 0, r = 0;
  for (int i = 1; i < n; i++) {
    int u, v, w;
    std::cin >> u >> v >> w;
    g[u].push_back({v, w});
    g[v].push_back({u, w});
    l = std::max(l, w);
  }
  lca.init(1, 0);
  for (int i = 1; i <= m; i++) {
    int u, v;
    std::cin >> u >> v;
    int a, b;
    std::tie(a, b) = lca(u, v);
    seg[i] = {u, v, a, b};
    r = std::max(r, b);
  }
  l = r - l;
  maxt = r;
  int ans = 0;
  while (l <= r) {
    int mid = (l + r) >> 1;
    if (check(mid)) {
      ans = mid;
      r = mid - 1;
    } else {
      l = mid + 1;
    }
  }
  std::cout << ans << '\n';
  return 0;
}
posted @ 2024-09-20 19:22  Unino  阅读(12)  评论(0)    收藏  举报