Article Index

洛谷 P2619 [国家集训队] Tree I 做题笔记

Published on

题意

给定无向带权连通图,每条边是黑色或白色,求恰好有 $need$ 条白边的生成树最小权。

$n = 5 \times 10^4, m = 10^5$

题解

二分一个 $k$,把所有白边的权值都加上 $k$,以达到调整最小生成树的白边数量。

注意有个坑,因为有边权相等的情况,所以跑生成树是有可能取不到刚好 $need$ 的。

代码

void STRUGGLING([[maybe_unused]] unsigned TEST_NUMBER) {
  tp n = bin, m = bin, need = bin, temp;
  vector<vector<tuple<tp, tp, tp>>> e(n);
  for (tp i = 0; i < m; ++i) {
    tp s, t, c, l; bin >> s >> t >> c >> l;
    e[s].emplace_back(t, c, !l);
  }
  tp l = -200, r = 200;
  auto check = [&]() -> tp {
    vector<tuple<tp, tp, tp, tp>> g;
    for (tp i = 0; i < n; ++i) {
      for (auto& [t, c, l] : e[i]) g.emplace_back(i, t, c, l);
    }
    stable_sort(FULL(g), [](const auto& a, const auto& b) { return make_pair(get<2>(a), get<3>(a)) < make_pair(get<2>(b), get<3>(b)); });
    lib::DSU d(n);
    temp = 0;
    tp white = 0;
    for (tp i = 0, cnt = -1; i < m && cnt != n; ++i) {
      if (d.root(get<0>(g[i])) != d.root(get<1>(g[i]))) {
        temp += get<2>(g[i]);
        d.link(get<0>(g[i]), get<1>(g[i]));
        ++cnt;
        white += get<3>(g[i]);
      }
    }
    return white;
  };
  while (l <= r) {
    tp mid = (l + r) / 2;
    for (auto& p : e) {
      for (auto& [t, c, l] : p) c += l * mid;
    }
    if (check() <= need) r = mid - 1;
    else l = mid + 1;
    for (auto& p : e) {
      for (auto& [t, c, l] : p) c -= l * mid;
    }
  }
  for (auto& p : e) {
    for (auto& [t, c, _l] : p) c += _l * l;
  }
  check();
  bin << temp - need * l << '\n';
}

void MIST() {
}

No comments

Post a comment