NOIP2018 提高组 赛道修建

贪心,双指针

算法 1

对于 \(m=1\),只有一条赛道,也就是树的直径。

算法 2

对于 \(b_i=a_i+1\),此时树退化为链,问题转化为将 \(n-1\) 长度的序列 \(a\) 找出 \(m\) 段,使得最小段长度最大。

算法 3

对于 \(a_i=1\),菊花图的情况。思路开始接近正解。其实我们也可以看成是一个序列,只能取 \(1 \sim 2\) 条。先将边长从大到小排序,对于最前面的,长度 \(\geq x\)\(x\) 为我们二分出的长度)的,记 \(+1\) 后跳过,剩下的我们需要选 \(2\) 条构成一个赛道。显然,假设最左边的是 \(l\),我们希望找到一个 \(r\),满足 \(l+r \geq x\)\(r\) 最小。因为已经排序,所以从右往左逐渐变大,于是从右往左开始走,直到遇到 \(r\) 满足要求。后面 \(l\) 会不断缩小,\(r\) 就应该随着增大,所以 \(r\) 不能往回走。当我们找不到 \(r\) 时结束,判断是否有 \(m\) 条赛道。时间复杂度为 \(O(n \log n+n)\)

到此,可以得到 \(55\) 分。

算法 4

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

考虑每棵子树的贡献。可以发现,如果我们不看子树的根节点到其父节点的边,与 \(a_i=1\) 时是相同的。然后我们尽可能选出一条最长的边,这条边作为该子树的贡献往上合并,且保证赛道数量不变,否则贡献为 \(0\)。时间复杂度为 \(O(n \log n \log w)\)

#include <bits/stdc++.h>

const int N = 5e4 + 5;

int n, m, tot;
std::vector<std::pair<int, int>> g[N];

int dfs(int u, int fa, int x) {
  std::vector<int> tmp;
  for (auto i : g[u]) {
    int v, w;
    std::tie(v, w) = i;
    if (v == fa) {
      continue;
    }
    int z = dfs(v, u, x);
    if (z != -1) {
      tmp.push_back(z + w);
    } else {
      tmp.push_back(w);
    }
  }
  int max = 0;
  std::sort(tmp.begin(), tmp.end(), std::greater<int>());
  int k = tmp.size();
  for (int i = 0, j = k - 1; i < k; i++) {
    if (tmp[i] >= x) {
      max++;
      continue;
    }
    while (i < j && tmp[i] + tmp[j] < x) {
      j--;
    }
    if (i >= j) {
      break;
    }
    max++;
    j--;
  }
  tot += max;
  int l = 0, r = k - 1, ans = -1;
  while (l <= r) {
    int mid = (l + r) >> 1;
    int res = 0;
    for (int i = 0, j = k - 1; i < k; i++) {
      if (i == mid) {
        continue;
      }
      if (tmp[i] >= x) {
        res++;
        continue;
      }
      while (i < j && tmp[i] + tmp[j] < x) {
        j--;
      }
      if (j == mid) {
        j--;
      }
      if (i >= j) {
        break;
      }
      res++;
      j--;
    }
    if (res == max) {
      ans = mid;
      r = mid - 1;
    } else {
      l = mid + 1;
    }
  }
  return ans == -1 ? ans : tmp[ans];
}

bool check(int x) {
  tot = 0;
  dfs(1, 0, x);
  return tot >= m;
}

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n >> m;
  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});
  }
  int l = 0, r = 5e8, ans = 0;
  while (l <= r) {
    int mid = (l + r) >> 1;
    if (check(mid)) {
      ans = mid;
      l = mid + 1;
    } else {
      r = mid - 1;
    }
  }
  std::cout << ans << '\n';
  return 0;
}
posted @ 2024-09-01 20:29  Unino  阅读(14)  评论(0)    收藏  举报