Problem p03108 (C++)

Buggy submission (status: Runtime Error):
#include <bits/stdc++.h>
class union_find {
public:
  using tp = std::vector<size_t>::size_type;
  // 取り扱う型.

  explicit union_find(const tp n)
      : parent(n), rank(n, 0), cache(), element(n, 1) {
    for (tp i = 0; i < n; i++) {
      parent[i] = i;
    }
    cache.reserve(n);
  }

  tp root(tp n) {
    // 番号nの親を求める. ついでに経路圧縮を行う.

    while (parent[n] != n) {
      // 根になるまで親を探索. ついでにキャッシュ変数に追加.
      cache.push_back(n);
      n = parent[n];
    }
    for (const auto i : cache) {
      // キャッシュ変数に追加された子の親を経路圧縮.
      parent[i] = n;
    }
    cache.clear();
    return n;
  }

  bool same(tp x, tp y) {
    // xとyが同じグループに属しているか計算.
    return root(x) == root(y);
  }

  tp unite(const tp x, const tp y) {
    // xとyを同じグループにする.
    const auto x_root = root(x);
    const auto y_root = root(y);

    if (x_root == y_root) {
      // 根が同じなら元から同じグループ.
      return 0;
    }
    const auto decrease = element[x_root] * element[y_root];
    if (rank[x_root] < rank[y_root]) {
      // rankの大きい方の根を統合後の根にする.
      parent[x_root] = y_root;
      element[y_root] += element[x_root];
    } else if (rank[y_root] < rank[x_root]) {
      // rankの大きい方の根を統合後の根にする.
      parent[y_root] = x_root;
      element[x_root] += element[y_root];
    } else {
      // rankが同じなら, どっちかを根にして根になったほうのランクを上げる.
      parent[x_root] = y_root;
      rank[x_root]++;
      element[y_root] += element[x_root];
    }
    return decrease;
  }

private:
  std::vector<tp> parent;
  // 親の番号を格納. 自分が根なら parent[i] == i になる.
  std::vector<tp> rank;
  // 自分の下に何段分の子があるかを格納.
  std::vector<tp> cache;
  // root計算のときに経路圧縮を行うためのキャッシュ変数.
  std::vector<tp> element;
  // グループの数.
};

int main() {
  uint64_t n, m;
  std::cin >> n >> m;

  std::vector<std::pair<uint64_t, uint64_t>> pair(m);

  for (auto &i : pair) {
    uint64_t a, b;
    std::cin >> a >> b;
    i = {a, b};
  }

  std::vector<uint64_t> cost(m);
  cost.back() = n * (n - 1) / 2;

  union_find uf(n);

  for (uint64_t i = 0; i < m - 1; i++) {
    const auto index = m - 1 - i;
    const auto dec = uf.unite(pair[index].first, pair[index].second);
    cost[index - 1] = cost[index] - dec;
  }

  for (const auto i : cost) {
    std::cout << i << std::endl;
  }
  return 0;
}


Fixed submission:
#include <bits/stdc++.h>
class union_find {
public:
  using tp = std::vector<size_t>::size_type;
  // 取り扱う型.

  explicit union_find(const tp n)
      : parent(n), rank(n, 0), cache(), element(n, 1) {
    for (tp i = 0; i < n; i++) {
      parent[i] = i;
    }
    cache.reserve(n);
  }

  tp root(tp n) {
    // 番号nの親を求める. ついでに経路圧縮を行う.

    while (parent[n] != n) {
      // 根になるまで親を探索. ついでにキャッシュ変数に追加.
      cache.push_back(n);
      n = parent[n];
    }
    for (const auto i : cache) {
      // キャッシュ変数に追加された子の親を経路圧縮.
      parent[i] = n;
    }
    cache.clear();
    return n;
  }

  bool same(tp x, tp y) {
    // xとyが同じグループに属しているか計算.
    return root(x) == root(y);
  }

  tp unite(const tp x, const tp y) {
    // xとyを同じグループにする.
    const auto x_root = root(x);
    const auto y_root = root(y);

    if (x_root == y_root) {
      // 根が同じなら元から同じグループ.
      return 0;
    }
    const auto decrease = element[x_root] * element[y_root];
    if (rank[x_root] < rank[y_root]) {
      // rankの大きい方の根を統合後の根にする.
      parent[x_root] = y_root;
      element[y_root] += element[x_root];
    } else if (rank[y_root] < rank[x_root]) {
      // rankの大きい方の根を統合後の根にする.
      parent[y_root] = x_root;
      element[x_root] += element[y_root];
    } else {
      // rankが同じなら, どっちかを根にして根になったほうのランクを上げる.
      parent[x_root] = y_root;
      rank[x_root]++;
      element[y_root] += element[x_root];
    }
    return decrease;
  }

private:
  std::vector<tp> parent;
  // 親の番号を格納. 自分が根なら parent[i] == i になる.
  std::vector<tp> rank;
  // 自分の下に何段分の子があるかを格納.
  std::vector<tp> cache;
  // root計算のときに経路圧縮を行うためのキャッシュ変数.
  std::vector<tp> element;
  // グループの数.
};

int main() {
  uint64_t n, m;
  std::cin >> n >> m;

  std::vector<std::pair<uint64_t, uint64_t>> pair(m);

  for (auto &i : pair) {
    uint64_t a, b;
    std::cin >> a >> b;
    i = {a, b};
  }

  std::vector<uint64_t> cost(m);
  cost.back() = n * (n - 1) / 2;

  union_find uf(n);

  for (uint64_t i = 0; i < m - 1; i++) {
    const auto index = m - 1 - i;
    const auto dec = uf.unite(pair[index].first - 1, pair[index].second - 1);
    cost[index - 1] = cost[index] - dec;
  }

  for (const auto i : cost) {
    std::cout << i << std::endl;
  }
  return 0;
}