제출 #1277592

#제출 시각아이디문제언어결과실행 시간메모리
1277592avighna조이터에서 친구를 만드는건 재밌어 (JOI20_joitter2)C++20
0 / 100
1 ms580 KiB
#include <iostream>
#include <numeric>
#include <queue>
#include <set>
#include <vector>

class joitter_dsu {
private:
  int n;
  std::vector<int> par;
  std::vector<int64_t> size;

  std::vector<std::set<int>> in, comps_in; // in[u]  = set of original nodes going into comp u
  // comps_in[u] = the same set as before, but instead of the original nodes we store components
  std::vector<std::set<std::pair<int, int>>> out; // out[u] = set of {u_orig, v} edges coming out of comp u
  std::queue<std::pair<int, int>> join_queue;

  int root(int u) { return u == par[u] ? u : par[u] = root(par[u]); }

  void merge(int u, int v) {
    u = root(u), v = root(v);
    if (u == v) {
      return;
    }
    par[v] = u;
    size[u] += size[v];
  }

  void erase_edge(int u_orig, int v_orig) {
    int u = root(u_orig), v = root(v_orig);
    in[v].erase(u_orig);
    out[u].erase({u_orig, v});
  }

  bool insert_edge(int u_orig, int v_orig) {
    int u = root(u_orig), v = root(v_orig);
    if (u == v or in[v].contains(u_orig)) {
      return false;
    }
    out[u].insert({u_orig, v});
    in[v].insert(u_orig);
    comps_in[v].insert(u);
    if (comps_in[u].contains(v)) {
      join_queue.push({u, v});
    }
    return true;
  }

  void join(int u, int v) {
    if (u == v) {
      return;
    }
    if (in[v].size() + out[v].size() > in[u].size() + out[u].size()) {
      std::swap(u, v);
    }
    ans -= in[u].size() * size[u] + size[u] * (size[u] - 1);
    ans -= in[v].size() * size[v] + size[v] * (size[v] - 1);
    std::vector<std::pair<int, int>> insert, erase;
    for (auto &[x, y] : out[v]) {
      erase.push_back({x, y});
      if (y != u) { // exclude internal edges
        insert.push_back({x, y});
      }
    }
    for (const int &i : in[v]) {
      erase.push_back({i, v});
      if (root(i) != u) { // same as above
        insert.push_back({i, v});
      }
    }
    for (auto &[x, y] : erase) {
      erase_edge(x, y);
    }
    merge(u, v);
    for (auto &[x, y] : insert) {
      insert_edge(x, y);
    }
    ans += in[u].size() * size[u] + size[u] * (size[u] - 1);
  }

public:
  int64_t ans;

  joitter_dsu(int n) : n(n), par(n), size(n, 1), in(n), comps_in(n), out(n), ans(0) {
    std::iota(par.begin(), par.end(), 0);
  }

  void add(int u, int v) {
    if (!insert_edge(u, v)) {
      return;
    }
    ans += size[root(v)];
    while (!join_queue.empty()) {
      auto [x, y] = join_queue.front();
      join_queue.pop();
      join(x, y);
    }
  }
};

int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);

  int n, m;
  std::cin >> n >> m;
  joitter_dsu dsu(n);
  for (int i = 0, u, v; i < m; ++i) {
    std::cin >> u >> v;
    dsu.add(u - 1, v - 1);
    std::cout << dsu.ans << '\n';
  }
}
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...