Problem p03108 (C++)

Buggy submission (status: Runtime Error):
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// しょっぱなやばい、とけなそう感･･･
// 方針たたんな
// 最後から見るか。
// 連結数はN(N-1)/2。最後は-1;
// 毎回すべての連結確認する、はむりむり
// 1[2,3,,,10] 2[3,4,,,10] 3[4,5,,,10]みたいな
// 和を足せばいい？
// んなことないか。1-2,1-3,2-3で1-2きれても3連結
// 孤立した島の積をとるか
// 1-2-3,4-5-6だったら3*3=9がだめ 3+3+3
// 1,2,3,4,5だったら？5*4/2 4+4+4+4+4)/2
// 1-2,3-4,5だったら8 3+3+3+3+4)/2
// はしを1本折るごとに探索してたんじゃ絶対むりむりちん10^5でしょ
// DP,どんなふうに・・・。
// 俺のもってる知識じゃまったくあてにならなそうだな。
// 解答みちゃう。
// ミカサがどうしたって?
// Union-Findすげえ。sizeを調べるのはどうしたらいいか
// 子どものsizeの配列をつねに用意。親に+1しとく
// 1-2でS[1]=2 S[2]=1
// 1-2-3 1-2-4 で、S[4]=1 S[3]=1 S[2]=3 S[1]=4みたいな感じか
// S[親]+=S[子1]+S[子2]か
struct UnionFind {
  vector<ll> par;
  vector<ll> siz;

  UnionFind(ll N) : par(N), siz(N, 1) {
    for (int i = 0; i < N; i++) {
      par[i] = i;
    }
  }

  ll root(ll x) {
    if (par[x] == x)
      return x;
    par[x] = root(par[x]);
  }

  void unite(ll x, ll y) {
    ll rx = root(x), ry = root(y);
    if (rx == ry)
      return;
    par[ry] = rx;       // x<yとしたい
    siz[rx] += siz[ry]; // 小さい数に集める
  }

  bool issame(ll x, ll y) { return root(x) == root(y); }

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

int main() {
  ll N, M;
  cin >> N >> M;
  vector<ll> A(M, 0), B(M, 0);
  for (int i = 0; i < M; i++) {
    ll a, b;
    cin >> a >> b;
    A[i] = a - 1;
    B[i] = b - 1;
  }
  UnionFind tree(N);
  ll all = N * (N - 1) / 2;
  vector<ll> ans(M, 0);
  for (int i = M - 1; i >= 0; i--) {
    ans[i] = all;
    if (all == 0)
      break;
    // 連結成分の大きさってなんだ
    // all-N1*N2をしたい
    // いみわかった。
    // 1 2-3 4 5 9 10-1*1
    // 1 2-3 4-5 8 9-1*1
    // 1-2-3 4-5 6 8-1*2
    // 1-2-3-4-5 0 6-3*2
    if (tree.issame(A[i], B[i]) == false) {
      all -= tree.size(A[i]) * tree.size(B[i]);
    }
    tree.unite(A[i], B[i]);
  }
  for (int i = 0; i < M; i++)
    cout << ans[i] << endl;
}


Fixed submission:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
// しょっぱなやばい、とけなそう感･･･
// 方針たたんな
// 最後から見るか。
// 連結数はN(N-1)/2。最後は-1;
// 毎回すべての連結確認する、はむりむり
// 1[2,3,,,10] 2[3,4,,,10] 3[4,5,,,10]みたいな
// 和を足せばいい？
// んなことないか。1-2,1-3,2-3で1-2きれても3連結
// 孤立した島の積をとるか
// 1-2-3,4-5-6だったら3*3=9がだめ 3+3+3
// 1,2,3,4,5だったら？5*4/2 4+4+4+4+4)/2
// 1-2,3-4,5だったら8 3+3+3+3+4)/2
// はしを1本折るごとに探索してたんじゃ絶対むりむりちん10^5でしょ
// DP,どんなふうに・・・。
// 俺のもってる知識じゃまったくあてにならなそうだな。
// 解答みちゃう。
// ミカサがどうしたって?
// Union-Findすげえ。sizeを調べるのはどうしたらいいか
// 子どものsizeの配列をつねに用意。親に+1しとく
// 1-2でS[1]=2 S[2]=1
// 1-2-3 1-2-4 で、S[4]=1 S[3]=1 S[2]=3 S[1]=4みたいな感じか
// S[親]+=S[子1]+S[子2]か
struct UnionFind {
  vector<ll> par;
  vector<ll> siz;

  UnionFind(ll N) : par(N), siz(N, 1) {
    for (int i = 0; i < N; i++) {
      par[i] = i;
    }
  }

  ll root(ll x) {
    if (par[x] == x)
      return x;
    par[x] = root(par[x]);
    return par[x];
  }

  void unite(ll x, ll y) {
    ll rx = root(x), ry = root(y);
    if (rx == ry)
      return;
    par[ry] = rx;       // x<yとしたい
    siz[rx] += siz[ry]; // 小さい数に集める
  }

  bool issame(ll x, ll y) { return root(x) == root(y); }

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

int main() {
  ll N, M;
  cin >> N >> M;
  vector<ll> A(M, 0), B(M, 0);
  for (int i = 0; i < M; i++) {
    ll a, b;
    cin >> a >> b;
    A[i] = a - 1;
    B[i] = b - 1;
  }
  UnionFind tree(N);
  ll all = N * (N - 1) / 2;
  vector<ll> ans(M, 0);
  for (int i = M - 1; i >= 0; i--) {
    ans[i] = all;
    if (all == 0)
      break;
    // 連結成分の大きさってなんだ
    // all-N1*N2をしたい
    // いみわかった。
    // 1 2-3 4 5 9 10-1*1
    // 1 2-3 4-5 8 9-1*1
    // 1-2-3 4-5 6 8-1*2
    // 1-2-3-4-5 0 6-3*2
    if (tree.issame(A[i], B[i]) == false) {
      all -= tree.size(A[i]) * tree.size(B[i]);
    }
    tree.unite(A[i], B[i]);
  }
  for (int i = 0; i < M; i++)
    cout << ans[i] << endl;
}