Problem p03108 (C++)

Buggy submission (status: Runtime Error):

#include <algorithm>
#include <bits/stdc++.h>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

using namespace std;
#define REP(i, n) FOR(i, 0, n)
#define FOR(i, a, b) for (long long i = (a), i##Len_ = (b); i < i##Len_; i++)
typedef long ll;

// abc120
// i番目の橋をとったときに、いくつの島が行き来できなくなるか
// 強固さ、みたいなのを保持しておく?
// 1-2 が切れても　1-3 3-1があればよい
// unionfind

struct UnionFind {

  vector<ll> par;

  // new
  UnionFind(ll n) : par(n, -1) {}

  // 初期化
  void init(ll n) { par.assign(n, -1); }

  ll root(ll x) {
    if (par[x] < 0)
      return x;
    else
      return par[x] = root(
                 par[x]); // 親の番号を返す(親が子になっていたら困るので取り込まれてたら代入も行う)
  }

  // 同じグループに属するか
  bool issame(ll x, ll y) {
    // rootが同じであるか
    return root(x) == root(y);
  }

  // 併合
  bool merge(ll x, ll y) {

    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (par[x] > par[y])
      swap(x, y);
    // 多い方に足し合わせる
    par[x] += par[y];
    // 従属(ここで0以上となる)
    par[y] = x;
    return true;
  }

  ll size(ll x) { return -par[root(x)]; }
};

int main() {

  std::ifstream in("resource/input.txt");
  std::cin.rdbuf(in.rdbuf());

  ll N, M;
  cin >> N >> M;
  // 橋の左をA 右をB
  vector<ll> A(M), B(M);
  REP(i, M) {
    cin >> A[i] >> B[i];
    // 0-based
    --A[i];
    --B[i];
  }

  UnionFind uf(N);

  // 橋の連結
  // はじめ、どの 2つの島についてもいくつかの橋を渡って互いに行き来できます。
  // はじめの連結数 N * (N-1) / 2  nC2
  ll cur = N * (N - 1) / 2;
  vector<ll> res;

  for (ll i = 0; i < M; ++i) {
    res.push_back(cur);

    // 後ろから調べる
    ll a = A[M - 1 - i];
    ll b = B[M - 1 - i];

    // 同じグループに属するか
    if (uf.issame(a, b))
      continue;

    // サイズ取得
    ll sa = uf.size(a);
    ll sb = uf.size(b);
    // 最初に外すのは、最後の橋→必ず(1*1)減る

    // 連結成分sa と 連結成分sb がここで初めて併合された
    cur -= sa * sb;
    uf.merge(a, b);
  }

  // 後ろからとってたので
  reverse(res.begin(), res.end());

  for (auto r : res) {
    cout << r << endl;
  }

  return 0;
}

Fixed submission:

#include <algorithm>
#include <bits/stdc++.h>
#include <iomanip>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

using namespace std;
#define REP(i, n) FOR(i, 0, n)
#define FOR(i, a, b) for (long long i = (a), i##Len_ = (b); i < i##Len_; i++)
typedef long ll;

// abc120
// i番目の橋をとったときに、いくつの島が行き来できなくなるか
// 強固さ、みたいなのを保持しておく?
// 1-2 が切れても　1-3 3-1があればよい
// unionfind

struct UnionFind {

  vector<ll> par;

  // new
  UnionFind(ll n) : par(n, -1) {}

  // 初期化
  void init(ll n) { par.assign(n, -1); }

  ll root(ll x) {
    if (par[x] < 0)
      return x;
    else
      return par[x] = root(
                 par[x]); // 親の番号を返す(親が子になっていたら困るので取り込まれてたら代入も行う)
  }

  // 同じグループに属するか
  bool issame(ll x, ll y) {
    // rootが同じであるか
    return root(x) == root(y);
  }

  // 併合
  bool merge(ll x, ll y) {

    x = root(x);
    y = root(y);
    if (x == y)
      return false;
    if (par[x] > par[y])
      swap(x, y);
    // 多い方に足し合わせる
    par[x] += par[y];
    // 従属(ここで0以上となる)
    par[y] = x;
    return true;
  }

  ll size(ll x) { return -par[root(x)]; }
};

int main() {

  ll N, M;
  cin >> N >> M;
  // 橋の左をA 右をB
  vector<ll> A(M), B(M);
  REP(i, M) {
    cin >> A[i] >> B[i];
    // 0-based
    --A[i];
    --B[i];
  }

  UnionFind uf(N);

  // 橋の連結
  // はじめ、どの 2つの島についてもいくつかの橋を渡って互いに行き来できます。
  // はじめの連結数 N * (N-1) / 2  nC2
  ll cur = N * (N - 1) / 2;
  vector<ll> res;

  for (ll i = 0; i < M; ++i) {
    res.push_back(cur);

    // 後ろから調べる
    ll a = A[M - 1 - i];
    ll b = B[M - 1 - i];

    // 同じグループに属するか
    if (uf.issame(a, b))
      continue;

    // サイズ取得
    ll sa = uf.size(a);
    ll sb = uf.size(b);
    // 最初に外すのは、最後の橋→必ず(1*1)減る

    // 連結成分sa と 連結成分sb がここで初めて併合された
    cur -= sa * sb;
    uf.merge(a, b);
  }

  // 後ろからとってたので
  reverse(res.begin(), res.end());

  for (auto r : res) {
    cout << r << endl;
  }

  return 0;
}