[APIO2019]桥梁

  • 给定边带权的无向图 \((V,E)\)。\(q\) 次操作,改变一条边的权值或者询问从 \(s_i\) 出发只走权值大于等于 \(w_i\) 的边能够到达的点数。
  • \(|V| \leq 5 \times 10^4, |E|, q \leq 10^5\) 。

分别考虑涉及修改和不涉及修改的边:

  • 对于不涉及修改的边,将询问从大到小排序,然后从大到小加边用并查集维护联通块。这部分是 \(O(|E| \log |E| + q \log q)\) 的。
  • 对于涉及修改的边,每次询问暴力访问询问之前的修改,满足条件就加入并查集。这部分是 \(O(q^2)\) 的。

将操作分块,设块大小为 \(S\),第一部分变为 \(O(|E|\log |E| + S \log S)\),第二部分变为 \(O(S^2)\)。

总复杂度 \(O(\frac qS|E|\log|E|+q\log S + qS)\),基本不等式大概算一下块大小,卡常。

#include <bits/stdc++.h>
#define dbg(...) std::cerr << "\033[32;1m", fprintf(stderr, __VA_ARGS__), std::cerr << "\033[0m"
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
using LL = long long;
using PII = std::pair<int, int>;

constexpr int N(5e4 + 5), S(500);
int fa[N], siz[N];
int find(int x) {
  while (fa[x] != x) x = fa[x];
  return x;
}
int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  int n, m, q;
  std::cin >> n >> m;
  std::vector<std::array<int, 3>> e(m);
  for (auto &[x, y, z] : e) std::cin >> x >> y >> z;
  std::vector<int> eq(m);
  std::iota(eq.begin(), eq.end(), 0);
  auto cmp_edge = [&](int i, int j) { return e[i][2] > e[j][2]; };
  std::sort(eq.begin(), eq.end(), cmp_edge);
  std::cin >> q;
  for (int i = 0; i < q; i += S) {
    std::vector<std::array<int, 3>> a, b;
    std::vector<char> vis(m);
    for (int j = 0; j < S && i + j < q; j++) {
      int t, x, y;
      std::cin >> t >> x >> y;
      if (t == 1) {
        vis[--x] = 1;
        a.push_back({ j, x, y });
      } else {
        b.push_back({ j, x, y });
      }
      (t == 1 ? a : b).push_back({ j, x, y });
    }
    std::sort(b.begin(), b.end(), [](const auto &x, const auto &y) { return x[2] > y[2]; });
    std::vector<int> c, d, ans(a.size() + b.size());
    for (int j = 0; j < m; j++) {
      if (!vis[eq[j]]) {
        c.push_back(eq[j]);
      } else {
        d.push_back(eq[j]);
      }
    }
    std::iota(fa, fa + n + 1, 0);
    std::fill(siz, siz + n + 1, 1);
    auto p = c.begin();
    for (auto &[t, s, w] : b) {
      for (; p != c.end() && e[*p][2] >= w; p++) {
        int x = find(e[*p][0]), y = find(e[*p][1]);
        if (x == y) continue;
        if (siz[x] > siz[y]) std::swap(x, y);
        fa[x] = y, siz[y] += siz[x];
      }
      
      std::vector<int> pre(d.size());
      for (int j = 0; j < d.size(); j++) {
        pre[j] = e[d[j]][2];
      }
      for (auto &[tt, x, y] : a) {
        if (tt > t) continue;
        e[x][2] = y;
      }
      std::vector<std::array<int, 3>> v;
      for (int j : d) {
        if (e[j][2] >= w) {
          int x = find(e[j][0]), y = find(e[j][1]);
          if (x == y) continue;
          if (siz[x] > siz[y]) std::swap(x, y);
          v.push_back({ x, y, fa[x] });
          fa[x] = y, siz[y] += siz[x];
        }
      }
      ans[t] = siz[find(s)];
      while (!v.empty()) {
        auto &[x, y, z] = v.back();
        fa[x] = z, siz[y] -= siz[x];
        v.pop_back();
      }
      for (int j = 0; j < d.size(); j++) {
        e[d[j]][2] = pre[j];
      }
    }
    for (int i : ans) if (i) std::cout << i << "\n";
    for (auto &[t, x, y] : a) e[x][2] = y;
    std::sort(d.begin(), d.end(), cmp_edge);
    std::merge(c.begin(), c.end(), d.begin(), d.end(), eq.begin(), cmp_edge);
  }
  return 0;
}

上一篇:可撤销并查集


下一篇:周鸿祎:保护“白帽子”合法权益 加强网络安全人才培养