Problem p03108 (C++)

Buggy submission (status: Runtime Error):
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
typedef long long ll;

#define REP(i, N) for (int i = 0; i < (N); i++)
#define REPP(i, a, b) for (int i = (a); i < (b); i++)
#define ALL(v) (v).begin(), (v).end()
#define RALL(v) (v).rbegin(), (v).rend()
#define VSORT(c) sort((c).begin(), (c).end())
#define SZ(x) ((int)(x).size())

using namespace std;
class UnionFind {

public:
  vector<int> Parent;

  // コンストラクタ
  UnionFind(int N) { Parent = vector<int>(N, -1); }

  // aの根を返す
  int root(int a) {
    if (Parent[a] < 0)
      return a;
    return Parent[a] = root(Parent[a]);
  }

  // 自分のいる木の頂点の数を返す関数
  int size(int a) { return -Parent[root(a)]; }

  // 二つの木が同じ場合はtrueを返す
  bool sameTree(int a, int b) {
    int ra = root(a);
    int rb = root(b);
    return ra == rb;
  }

  // 異なる二つの木を結合する
  void unite(int a, int b) {
    int ra = root(a);
    int rb = root(b);
    if (ra == rb)
      return;

    if (size(ra) < size(rb))
      swap(ra, rb);

    Parent[ra] += Parent[rb];
    Parent[rb] = a;
  }
};

int main() {

  cin.tie(0);
  ios::sync_with_stdio(false);

  int N, M;
  cin >> N >> M;
  vector<int> A(M), B(M);

  for (int i = 0; i < M; i++) {
    cin >> A[i] >> B[i];
    // 配列を用意しているのは0-Nまでだからそこの位置に合わせている
    A[i]--;
    B[i]--;
  }

  vector<long long> ans(M);
  // 引き始めの初期値はNC2を採用(Nこの中から2個を選択する繋げからからそのパターン通り存在)
  ans[M - 1] = (ll)N * (N - 1) / 2;

  UnionFind Uni(N);
  for (int i = M - 1; i > 0; --i) {
    ans[i - 1] = ans[i];
    if (!Uni.sameTree(A[i], B[i])) {
      ans[i - 1] -= (ll)Uni.size(A[i]) * Uni.size(B[i]);
      Uni.unite(A[i], B[i]);
    }
  }
  for (int i = 0; i < M; i++) {
    cout << ans[i] << endl;
  }

  return 0;
}


Fixed submission:
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <functional>
#include <iostream>
#include <list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <vector>
typedef long long ll;

#define REP(i, N) for (int i = 0; i < (N); i++)
#define REPP(i, a, b) for (int i = (a); i < (b); i++)
#define ALL(v) (v).begin(), (v).end()
#define RALL(v) (v).rbegin(), (v).rend()
#define VSORT(c) sort((c).begin(), (c).end())
#define SZ(x) ((int)(x).size())

using namespace std;
class UnionFind {

public:
  vector<int> Parent;

  // コンストラクタ
  UnionFind(int N) { Parent = vector<int>(N, -1); }

  // aの根を返す
  int root(int a) {
    if (Parent[a] < 0)
      return a;
    return Parent[a] = root(Parent[a]);
  }

  // 自分のいる木の頂点の数を返す関数
  int size(int a) { return -Parent[root(a)]; }

  // 二つの木が同じ場合はtrueを返す
  bool sameTree(int a, int b) {
    int ra = root(a);
    int rb = root(b);
    return ra == rb;
  }

  // 異なる二つの木を結合する
  void unite(int a, int b) {
    int ra = root(a);
    int rb = root(b);
    if (ra == rb)
      return;

    if (size(ra) < size(rb))
      swap(ra, rb);

    Parent[ra] += Parent[rb];
    Parent[rb] = ra;
  }
};

int main() {

  cin.tie(0);
  ios::sync_with_stdio(false);

  int N, M;
  cin >> N >> M;
  vector<int> A(M), B(M);

  for (int i = 0; i < M; i++) {
    cin >> A[i] >> B[i];
    // 配列を用意しているのは0-Nまでだからそこの位置に合わせている
    A[i]--;
    B[i]--;
  }

  vector<long long> ans(M);
  // 引き始めの初期値はNC2を採用(Nこの中から2個を選択する繋げからからそのパターン通り存在)
  ans[M - 1] = (ll)N * (N - 1) / 2;

  UnionFind Uni(N);
  for (int i = M - 1; i > 0; --i) {
    ans[i - 1] = ans[i];
    if (!Uni.sameTree(A[i], B[i])) {
      ans[i - 1] -= (ll)Uni.size(A[i]) * Uni.size(B[i]);
      Uni.unite(A[i], B[i]);
    }
  }
  for (int i = 0; i < M; i++) {
    cout << ans[i] << endl;
  }

  return 0;
}