Problem p02573 (C++) — buggy submission, verdict: Runtime Error

#include <iostream>
#include <vector>

using namespace std;

/****************************************************************************/
// メモ - UnionFindとは -
//
//      UnionFindは、グループを管理するときに便利なグラフのひとつ。
//      グループをは、グループの代表（以降、「親」という）を決め、
//     「〇〇が属するグループ」と管理する。
//      この時、グラフ（木）の形になる。
//
//      このclassでは0からnのノードを持つUnionFind木を作成する。
//      内部構造を知らなくても、ノードの数さえ知っていれば一連の操作が可能
//
//      このclassの操作関数は、
//          UniTree
//              UniTree型の変数を作る関数（コンストラクター）
//
//          int FindSet(int x)
//              ノードxの親を求める関数
//
//          bool Same(int x, int y)
//              ノードxとノードyが同じにグループに
//              所属しているかを返す関数
//
//          void Unite(int x, int y)
//              ノードxとノードyを同じグループに
//              所属させる関数
//
//      以上4つである。必要であれば適宜追加してね。
/****************************************************************************/
class UniTree {
public:
  // par[i]       : ノードiの親
  // hight[i]     : ノードiの高さ
  vector<int> par, hight;

  //  [引数]
  //          int size            : UnionFind木の大きさ
  //
  //  [機能]
  //          UnionFind木の初期化（親と高さの初期化）
  UniTree() {}
  UniTree(int size) {
    par = vector<int>(size + 1, 0);
    hight = vector<int>(size + 1, 0);
    for (int i = 0; i <= size; i++) {
      par[i] = i;
    }
  }

  //  [引数]
  //          int x   : 親を求めたいノードの番号
  //
  //  [機能]
  //          ノードxの親を求めながら、再帰的に経路圧縮を行う
  int FindSet(int x) {
    if (par[x] != x) {
      par[x] = FindSet(par[x]);
    }

    return (par[x]);
  }

  //  [引数]
  //          int x   : ノードx
  //          int y   : ノードy
  //
  //  [機能]
  //          ノードxとノードyが同じグループならtrue、
  //          そうでなければfalseを返す
  bool Same(int x, int y) {
    int X, Y;

    X = FindSet(x);
    Y = FindSet(y);

    if (X == Y) {
      return (true);
    } else {
      return (false);
    }
  }

  //  [引数]
  //          int x   : ノードx
  //          int y   : ノードy
  //
  //  [機能]
  //          ノードxとノードyが同じグループに属するようにする
  void Link(int x, int y) {
    int X, Y;

    X = FindSet(x);
    Y = FindSet(y);

    if (hight[X] > hight[Y]) {
      par[Y] = X;
    } else if (hight[Y] > hight[X]) {
      par[X] = Y;
    } else {
      par[X] = Y;
      hight[Y] = X;
    }

    return;
  }

  /****************************************************************************/
  // メモ - UnionFindのメソッド （操作関数群） -
  //
  // UnionFind(int size)
  //  UnionFind型の変数を作る関数（コンストラクター）。
  //  作る時は、
  //
  //  『UniTree 変数名 = UniTree 大きさ;』
  //     で作れる。
  //
  //   詳しい構造はよく知らない。螺旋本を丸パクリした。
  //  どうやらclassでコンストラクター（ユーザー定義型の変数を作るメソッド）は、
  //  class名と同じ名前でないといけないらしい。
  //
  //  初期化について
  //    hight(高さ)をすべて0にしたのは、すべてのノードの下にノードがないから。
  //    par（親）を自分自身にしたのは、すべてのノードが『自分自身が親のグループ』、
  //    つまり自分しかそのグループに所属していないから。
  // ---------------------------------------------------------------------------
  //
  // int FindSet(int x)
  //   ノードxの親を求める関数。
  //   これ以降のコンストラクターではない関数を呼び出すときは、
  //
  //   『変数名.FindSet(ノードの番号）』
  //     で呼び出せる。
  //
  //  「ノードxの親はノードy」→「ノードyの親はノードz」→「ノードzの親はノードz」
  //   したがって、「ノードxの親はノードz」となる。
  //   ノードxの親を求めると同時に、ノードxの親の更新を使っている
  //   いつも『自分の親』の親が自分自身とは限らない。
  //
  //   経路圧縮について
  //     経路圧縮は、グループの構造は壊れず、次に親を求める時に高速になるので
  //     積極的にしよう。（グラフの形は死んでしまう）
  // ---------------------------------------------------------------------------
  //
  // bool Same(int x, int y)
  //   ノードxとノードyが同じグループに属しているなら"true"、
  //   そうでなければ"false"を返す。
  //   ノードxの親とノードyの親が同じなら、同じグループに属していると言える
  //
  //   ちなみに、ノードxの親をX、ノードyの親をYとしてるけど、
  //   C/C++ では変数名に大文字を使わないほうがいい。
  // ---------------------------------------------------------------------------
  //
  // void Unite(int x, int y)
  //   ノードxとノードyを同じグループに所属させる。
  //   「『ノードxの親』の親をノードyの親にする」もしくは、
  //   「『ノードyの親』の親をノードxの親にする」
  //    という言い方のほうが正しいかもしれない。
  //
  //    効率化のため、ノードx,yのうち、そこまでの高さが高いほうが親となる
  //    高さが同じだった時も高さの更新している
  //
  /****************************************************************************/
};

signed main() {
  int n, q, com, a, b, ans[200005] = {};
  int ans1 = 0, find[200005];
  cin >> n >> q;
  UniTree tree = UniTree(n);

  for (int i = 0; i < q; i++) {
    cin >> a >> b;
    tree.Link(a, b);
  }
  for (int i = 0; i < n; i++) {
    find[i] = tree.FindSet(i + 1);
  }
  for (int i = 0; i < n; i++) {
    ans[find[i]]++;
    if (ans1 < ans[find[i]]) {
      ans1 = ans[find[i]];
    }
  }
  cout << ans1 << endl;
  return (0);
}

Error: 0

Fixed submission:

#include <iostream>
#include <vector>

using namespace std;

/****************************************************************************/
// メモ - UnionFindとは -
//
//      UnionFindは、グループを管理するときに便利なグラフのひとつ。
//      グループをは、グループの代表（以降、「親」という）を決め、
//     「〇〇が属するグループ」と管理する。
//      この時、グラフ（木）の形になる。
//
//      このclassでは0からnのノードを持つUnionFind木を作成する。
//      内部構造を知らなくても、ノードの数さえ知っていれば一連の操作が可能
//
//      このclassの操作関数は、
//          UniTree
//              UniTree型の変数を作る関数（コンストラクター）
//
//          int FindSet(int x)
//              ノードxの親を求める関数
//
//          bool Same(int x, int y)
//              ノードxとノードyが同じにグループに
//              所属しているかを返す関数
//
//          void Unite(int x, int y)
//              ノードxとノードyを同じグループに
//              所属させる関数
//
//      以上4つである。必要であれば適宜追加してね。
/****************************************************************************/
class UniTree {
public:
  // par[i]       : ノードiの親
  // hight[i]     : ノードiの高さ
  vector<int> par, hight;

  //  [引数]
  //          int size            : UnionFind木の大きさ
  //
  //  [機能]
  //          UnionFind木の初期化（親と高さの初期化）
  UniTree() {}
  UniTree(int size) {
    par = vector<int>(size + 1, 0);
    hight = vector<int>(size + 1, 0);
    for (int i = 0; i <= size; i++) {
      par[i] = i;
    }
  }

  //  [引数]
  //          int x   : 親を求めたいノードの番号
  //
  //  [機能]
  //          ノードxの親を求めながら、再帰的に経路圧縮を行う
  int FindSet(int x) {
    if (par[x] != x) {
      par[x] = FindSet(par[x]);
    }

    return (par[x]);
  }

  //  [引数]
  //          int x   : ノードx
  //          int y   : ノードy
  //
  //  [機能]
  //          ノードxとノードyが同じグループならtrue、
  //          そうでなければfalseを返す
  bool Same(int x, int y) {
    int X, Y;

    X = FindSet(x);
    Y = FindSet(y);

    if (X == Y) {
      return (true);
    } else {
      return (false);
    }
  }

  //  [引数]
  //          int x   : ノードx
  //          int y   : ノードy
  //
  //  [機能]
  //          ノードxとノードyが同じグループに属するようにする
  void Link(int x, int y) {
    int X, Y;

    X = FindSet(x);
    Y = FindSet(y);

    if (hight[X] > hight[Y]) {
      par[Y] = X;
    } else if (hight[Y] > hight[X]) {
      par[X] = Y;
    } else {
      par[X] = Y;
      hight[Y] = X;
    }

    return;
  }

  /****************************************************************************/
  // メモ - UnionFindのメソッド （操作関数群） -
  //
  // UnionFind(int size)
  //  UnionFind型の変数を作る関数（コンストラクター）。
  //  作る時は、
  //
  //  『UniTree 変数名 = UniTree 大きさ;』
  //     で作れる。
  //
  //   詳しい構造はよく知らない。螺旋本を丸パクリした。
  //  どうやらclassでコンストラクター（ユーザー定義型の変数を作るメソッド）は、
  //  class名と同じ名前でないといけないらしい。
  //
  //  初期化について
  //    hight(高さ)をすべて0にしたのは、すべてのノードの下にノードがないから。
  //    par（親）を自分自身にしたのは、すべてのノードが『自分自身が親のグループ』、
  //    つまり自分しかそのグループに所属していないから。
  // ---------------------------------------------------------------------------
  //
  // int FindSet(int x)
  //   ノードxの親を求める関数。
  //   これ以降のコンストラクターではない関数を呼び出すときは、
  //
  //   『変数名.FindSet(ノードの番号）』
  //     で呼び出せる。
  //
  //  「ノードxの親はノードy」→「ノードyの親はノードz」→「ノードzの親はノードz」
  //   したがって、「ノードxの親はノードz」となる。
  //   ノードxの親を求めると同時に、ノードxの親の更新を使っている
  //   いつも『自分の親』の親が自分自身とは限らない。
  //
  //   経路圧縮について
  //     経路圧縮は、グループの構造は壊れず、次に親を求める時に高速になるので
  //     積極的にしよう。（グラフの形は死んでしまう）
  // ---------------------------------------------------------------------------
  //
  // bool Same(int x, int y)
  //   ノードxとノードyが同じグループに属しているなら"true"、
  //   そうでなければ"false"を返す。
  //   ノードxの親とノードyの親が同じなら、同じグループに属していると言える
  //
  //   ちなみに、ノードxの親をX、ノードyの親をYとしてるけど、
  //   C/C++ では変数名に大文字を使わないほうがいい。
  // ---------------------------------------------------------------------------
  //
  // void Unite(int x, int y)
  //   ノードxとノードyを同じグループに所属させる。
  //   「『ノードxの親』の親をノードyの親にする」もしくは、
  //   「『ノードyの親』の親をノードxの親にする」
  //    という言い方のほうが正しいかもしれない。
  //
  //    効率化のため、ノードx,yのうち、そこまでの高さが高いほうが親となる
  //    高さが同じだった時も高さの更新している
  //
  /****************************************************************************/
};

signed main() {
  int n, q, com, a, b, ans[100005] = {};
  int ans1 = 0, find[100005];
  cin >> n >> q;
  UniTree tree = UniTree(n);

  for (int i = 0; i < q; i++) {
    cin >> a >> b;
    tree.Link(a, b);
  }
  for (int i = 0; i < n; i++) {
    find[i] = tree.FindSet(i + 1);
  }
  for (int i = 0; i < n; i++) {
    ans[find[i]]++;
    if (ans1 < ans[find[i]]) {
      ans1 = ans[find[i]];
    }
  }
  cout << ans1 << endl;
  return (0);
}