Problem p03108 (C++)

Buggy submission (status: Time Limit Exceeded):
#include <algorithm>
#include <bits/stdc++.h>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <queue>
#include <string>
#include <tuple>
#include <vector>

#define rep(i, n) for (ll i = 0; i < n; i++)
#define dup(x, y) (((x) + (y)-1) / (y)) // dup * y >= x なる最小のdup.
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

using Graph = vector<vector<ll>>;
//	std::cout<<std::fixed<<std::setprecision(10);

ll par[100000]; // par[i]... iの親
ll mem[100000]; // iの(自分も含めた) 子の構成島数

ll root(ll x) {
  // 自身が親と同一の時まで再帰的にrootをたどる
  if (par[x] == x)
    return x;
  else {
    return root(par[x]);
  }
}

void unite(ll x, ll y) {
  ll rx = root(x);
  ll ry = root(y);
  if (rx == ry)
    return;
  else {
    mem[ry] += mem[rx];
    par[rx] = ry;
    return;
  }
}

int main() {
  ull N, M;
  cin >> N >> M;
  ull A[M], B[M];
  rep(i, M) {
    ull a, b;
    cin >> a >> b;
    A[i] = a - 1;
    B[i] = b - 1;
  }

  /* Uion-find というデータ構造を使えば処理時間を短縮できるらしい */
  // cf. https://qiita.com/ofutonfuton/items/c17dfd33fc542c222396
  // cf. https://atc001.contest.atcoder.jp/tasks/unionfind_a

  rep(i, N) par[i] = i; // 最初は全て自分が親
  rep(i, N) mem[i] = 1;

  ////////////////////

  ull ans[M];                   // ans
  ans[M - 1] = N * (N - 1) / 2; // 全ての橋が崩壊した時の不便さ
  for (ll i = M - 1; i > 0; i--) {
    ll ra = root(A[i]);
    ll rb = root(B[i]);
    if (ra == rb)
      ans[i - 1] = ans[i];
    else {
      ans[i - 1] = ans[i] - mem[ra] * mem[rb];
      // cout << "log:" << ans[i-1] << endl;
      // cout << "A, B " << A[i] << ' ' << B[i] << endl;
      // cout << "rA, rB " << root(A[i]) << ' ' << root(B[i]) << endl;
      // cout << "mrA, mrB " << mem[root(A[i])] << ' ' << mem[root(B[i])] <<
      // endl;
      unite(rb, ra);
    }
  }

  rep(i, M) { cout << ans[i] << endl; }

  return 0;
}


Fixed submission:
#include <algorithm>
#include <bits/stdc++.h>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <queue>
#include <string>
#include <tuple>
#include <vector>

#define rep(i, n) for (ll i = 0; i < n; i++)
#define dup(x, y) (((x) + (y)-1) / (y)) // dup * y >= x なる最小のdup.
using namespace std;
typedef long long ll;
typedef unsigned long long ull;

using Graph = vector<vector<ll>>;
//	std::cout<<std::fixed<<std::setprecision(10);

ll par[100000]; // par[i]... iの親
ll mem[100000]; // iの(自分も含めた) 子の構成島数

ll root(ll x) {
  // 自身が親と同一の時まで再帰的にrootをたどる
  if (par[x] == x)
    return x;
  else {
    return root(par[x]);
  }
}

void unite(ll x, ll y) {
  ll rx = root(x);
  ll ry = root(y);
  if (rx == ry)
    return;
  else {
    if (rx > ry) {
      ll tmp = rx;
      rx = ry;
      ry = tmp;
    }
    mem[ry] += mem[rx];
    par[rx] = ry;
    return;
  }
}

int main() {
  ull N, M;
  cin >> N >> M;
  ull A[M], B[M];
  rep(i, M) {
    ull a, b;
    cin >> a >> b;
    A[i] = a - 1;
    B[i] = b - 1;
  }

  /* Uion-find というデータ構造を使えば処理時間を短縮できるらしい */
  // cf. https://qiita.com/ofutonfuton/items/c17dfd33fc542c222396
  // cf. https://atc001.contest.atcoder.jp/tasks/unionfind_a

  rep(i, N) par[i] = i; // 最初は全て自分が親
  rep(i, N) mem[i] = 1;

  ////////////////////

  ull ans[M];                   // ans
  ans[M - 1] = N * (N - 1) / 2; // 全ての橋が崩壊した時の不便さ
  for (ll i = M - 1; i > 0; i--) {
    ll ra = root(A[i]);
    ll rb = root(B[i]);
    if (ra == rb)
      ans[i - 1] = ans[i];
    else {
      ans[i - 1] = ans[i] - mem[ra] * mem[rb];
      // cout << "log:" << ans[i-1] << endl;
      // cout << "A, B " << A[i] << ' ' << B[i] << endl;
      // cout << "rA, rB " << root(A[i]) << ' ' << root(B[i]) << endl;
      // cout << "mrA, mrB " << mem[root(A[i])] << ' ' << mem[root(B[i])] <<
      // endl;
      unite(rb, ra);
    }
  }

  rep(i, M) { cout << ans[i] << endl; }

  return 0;
}