Problem p03108 (C++)

Buggy submission (status: Runtime Error):
#include <iostream>
using namespace std;

#ifdef DEBUG
#define IFD if (true)
#else
#define IFD if (false)
#endif

#include <vector>

// 要素個数カウント付き Union Find

// 要素の型。int か long あたりを想定
template <typename T_uf_data>

class UnionFind {
private:
  std::vector<T_uf_data> par; // 親 (parent) を指す
  std::vector<T_uf_data> rnk;
  std::vector<T_uf_data>
      cnt; // 引数の値が含まれるグループの要素数（ルートノードのみが意味を持つ）

  // その値を持つルートノードを返す
  T_uf_data find_root(int x) {
    if (par[x] == x) {
      return x;
    } else {
      return par[x] = find_root(par[x]);
    }
  }

public:
  // コンストラクタの引数で要素数を指定
  UnionFind(int n) : par(n), rnk(n), cnt(n) {
    for (int i = 0; i < n; i++) {
      par[i] = i; // 親を持たないノードは自分自身を親としておく
      rnk[i] = 0;
      cnt[i] = 1; // 初期状態ではすべてのノードの要素数は 1
    }
  }

  // 二つの要素をグループ化
  void Unite(int x, int y) {
    x = find_root(x);
    y = find_root(y);
    if (x == y)
      return;

    if (rnk[x] < rnk[y]) {
      // y が親になる
      par[x] = y;
      cnt[y] += cnt[x];
    } else {
      // x が親になる
      par[y] = x;
      cnt[x] += cnt[y];
      if (rnk[x] == rnk[y])
        rnk[x]++;
    }
  }

  // x と y が同じグループかを返す
  bool isSameGroup(int x, int y) { return find_root(x) == find_root(y); }

  // x を含むグループの要素数を返す
  int Size(int x) { return cnt[find_root(x)]; }
};

int main() {
  int N, M, A[100001], B[100001];
  long long ans[100001];

  cin >> N >> M;
  UnionFind<int> uf(M + 1);

  for (int i = 1; i <= M; i++) {
    cin >> A[i] >> B[i];
  }
  ans[M] = (long long)N * (N - 1) / 2;

  for (int i = M - 1; i >= 1; i--) {
    ans[i] = ans[i + 1];
    if (!uf.isSameGroup(A[i + 1], B[i + 1])) {
      ans[i] -= (long long)uf.Size(A[i + 1]) * uf.Size(B[i + 1]);
      uf.Unite(A[i + 1], B[i + 1]);
    }
  }

  for (int i = 1; i <= M; i++) {
    cout << ans[i] << endl;
  }

  return 0;
}


Fixed submission:
#include <iostream>
using namespace std;

#ifdef DEBUG
#define IFD if (true)
#else
#define IFD if (false)
#endif

#include <vector>

// 要素個数カウント付き Union Find

// 要素の型。int か long あたりを想定
template <typename T_uf_data>

class UnionFind {
private:
  std::vector<T_uf_data> par; // 親 (parent) を指す
  std::vector<T_uf_data> rnk;
  std::vector<T_uf_data>
      cnt; // 引数の値が含まれるグループの要素数（ルートノードのみが意味を持つ）

  // その値を持つルートノードを返す
  T_uf_data find_root(int x) {
    if (par[x] == x) {
      return x;
    } else {
      return par[x] = find_root(par[x]);
    }
  }

public:
  // コンストラクタの引数で要素数を指定
  UnionFind(int n) : par(n), rnk(n), cnt(n) {
    for (int i = 0; i < n; i++) {
      par[i] = i; // 親を持たないノードは自分自身を親としておく
      rnk[i] = 0;
      cnt[i] = 1; // 初期状態ではすべてのノードの要素数は 1
    }
  }

  // 二つの要素をグループ化
  void Unite(int x, int y) {
    x = find_root(x);
    y = find_root(y);
    if (x == y)
      return;

    if (rnk[x] < rnk[y]) {
      // y が親になる
      par[x] = y;
      cnt[y] += cnt[x];
    } else {
      // x が親になる
      par[y] = x;
      cnt[x] += cnt[y];
      if (rnk[x] == rnk[y])
        rnk[x]++;
    }
  }

  // x と y が同じグループかを返す
  bool isSameGroup(int x, int y) { return find_root(x) == find_root(y); }

  // x を含むグループの要素数を返す
  int Size(int x) { return cnt[find_root(x)]; }
};

int main() {
  int N, M, A[100001], B[100001];
  long long ans[100001];

  cin >> N >> M;
  UnionFind<int> uf(100001);

  for (int i = 1; i <= M; i++) {
    cin >> A[i] >> B[i];
  }
  ans[M] = (long long)N * (N - 1) / 2;

  for (int i = M - 1; i >= 1; i--) {
    ans[i] = ans[i + 1];
    if (!uf.isSameGroup(A[i + 1], B[i + 1])) {
      ans[i] -= (long long)uf.Size(A[i + 1]) * uf.Size(B[i + 1]);
      uf.Unite(A[i + 1], B[i + 1]);
    }
  }

  for (int i = 1; i <= M; i++) {
    cout << ans[i] << endl;
  }

  return 0;
}