Problem p03108 (C++)

Buggy submission (status: Runtime Error):
#include <algorithm>
#include <deque>
#include <iomanip>
#include <iostream>
#include <map>
#include <math.h>
#include <queue>
#include <stack>
#include <string>
#include <vector>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef int itn;
const ll LINF = 1e18;
const int INF = 1e8;

// マクロ定義
#define vvint(vec, n, m, l)                                                    \
  vector<vector<int>> vec(n, vector<int>(m, l)); // lで初期化
#define vvll(vec, n, m, l) vector<vector<ll>> vec(n, vector<ll>(m, l));
#define vint vector<int>
#define pint pair<int, int>
#define rep(i, a) for (int i = 0; i < (a); i++)
#define all(x) (x).begin(), (x).end()
#define debug system("pause") // デバッグ用

class UnionFind {
public:
  vector<ll> par; // 各元の親を表す配列
  vector<ll> siz; // 素集合のサイズを表す配列(1 で初期化)

  // Constructor
  UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }
  void init(ll sz_) {
    par.resize(sz_);
    siz.assign(sz_, 1LL); // resize だとなぜか初期化されなかった
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }

  // Member Function
  // Find
  ll root(ll x) { // 根の検索
    while (par[x] != x) {
      x = par[x] = par[par[x]]; // x の親の親を x の親とする
    }
    return x;
  }

  // Union(Unite, Merge)
  bool merge(ll x, ll y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    // merge technique（データ構造をマージするテク．小を大にくっつける）
    if (siz[x] < siz[y])
      swap(x, y);
    siz[x] += siz[y];
    par[y] = x;
    return true;
  }

  bool isSame(ll x, ll y) { // 連結判定
    return root(x) == root(y);
  }

  ll size(ll x) { // 素集合のサイズ
    return siz[root(x)];
  }
};

int main(void) {
  cin.tie(0);
  ios::sync_with_stdio(false);

  ll n, m;
  cin >> n >> m;

  vector<ll> a(m), b(m);

  rep(i, m) { cin >> a[i] >> b[i]; }

  UnionFind tree(n);
  vector<ll> ans(m);
  ans[m - 1] = n * (n - 1);
  ans[m - 1] /= 2;

  for (int i = m - 2; i >= 0; i--) {
    if (!tree.isSame(a[i + 1], b[i + 1])) {
      ans[i] = ans[i + 1] - tree.size(a[i + 1]) * tree.size(b[i + 1]);
      tree.merge(a[i + 1], b[i + 1]);
    } else {
      ans[i] = ans[i + 1];
    }
  }
  rep(i, m) { cout << ans[i] << endl; }
}

Fixed submission:
#include <algorithm>
#include <deque>
#include <iomanip>
#include <iostream>
#include <map>
#include <math.h>
#include <queue>
#include <stack>
#include <string>
#include <vector>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef int itn;
const ll LINF = 1e18;
const int INF = 1e8;

// マクロ定義
#define vvint(vec, n, m, l)                                                    \
  vector<vector<int>> vec(n, vector<int>(m, l)); // lで初期化
#define vvll(vec, n, m, l) vector<vector<ll>> vec(n, vector<ll>(m, l));
#define vint vector<int>
#define pint pair<int, int>
#define rep(i, a) for (int i = 0; i < (a); i++)
#define all(x) (x).begin(), (x).end()
#define debug system("pause") // デバッグ用

class UnionFind {
public:
  vector<ll> par; // 各元の親を表す配列
  vector<ll> siz; // 素集合のサイズを表す配列(1 で初期化)

  // Constructor
  UnionFind(ll sz_) : par(sz_), siz(sz_, 1LL) {
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }
  void init(ll sz_) {
    par.resize(sz_);
    siz.assign(sz_, 1LL); // resize だとなぜか初期化されなかった
    for (ll i = 0; i < sz_; ++i)
      par[i] = i; // 初期では親は自分自身
  }

  // Member Function
  // Find
  ll root(ll x) { // 根の検索
    while (par[x] != x) {
      x = par[x] = par[par[x]]; // x の親の親を x の親とする
    }
    return x;
  }

  // Union(Unite, Merge)
  bool merge(ll x, ll y) {
    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    // merge technique（データ構造をマージするテク．小を大にくっつける）
    if (siz[x] < siz[y])
      swap(x, y);
    siz[x] += siz[y];
    par[y] = x;
    return true;
  }

  bool isSame(ll x, ll y) { // 連結判定
    return root(x) == root(y);
  }

  ll size(ll x) { // 素集合のサイズ
    return siz[root(x)];
  }
};

int main(void) {
  cin.tie(0);
  ios::sync_with_stdio(false);

  ll n, m;
  cin >> n >> m;

  vector<ll> a(m), b(m);

  rep(i, m) { cin >> a[i] >> b[i]; }

  UnionFind tree(n);
  vector<ll> ans(m);
  ans[m - 1] = n * (n - 1);
  ans[m - 1] /= 2;

  for (int i = m - 2; i >= 0; i--) {
    if (!tree.isSame(a[i + 1] - 1, b[i + 1] - 1)) {
      ans[i] = ans[i + 1] - tree.size(a[i + 1] - 1) * tree.size(b[i + 1] - 1);
      tree.merge(a[i + 1] - 1, b[i + 1] - 1);
    } else {
      ans[i] = ans[i + 1];
    }
  }
  rep(i, m) { cout << ans[i] << endl; }
}