Problem:
Score : 100 points

Problem Statement

You are given an undirected graph with N vertices and 0 edges. Process Q queries of the following types.

0 u v: Add an edge (u, v).

1 u v: Print 1 if u and v are in the same connected component, 0 otherwise.

Constraints

1 \leq N \leq 200,000

1 \leq Q \leq 200,000

0 \leq u_i, v_i \lt N

Input

Input is given from Standard Input in the following format:

N Q
t_1 u_1 v_1
t_2 u_2 v_2
:
t_Q u_Q v_Q

出力

For each query of the latter type, print the answer.

Sample Input 1

4 7
1 0 1
0 0 1
0 2 3
1 0 1
1 1 2
0 0 2
1 1 3

Sample Output 1

0
1
0
1

Solution in PHP (7.4.4) (judge verdict: Accepted)

<?php

/**
 * Class DSU
 * ref. AtCoder Library
 * 無向グラフに対して、<br>
 * 1. 辺の追加<br>
 * 2. 2頂点が連結かの判定<br>
 * をならし O(α(n)) 時間で処理することが出来ます。<br>
 * また、内部的に各連結成分ごとに代表となる頂点を 1つ持っています。<br>
 * 辺の追加により連結成分がマージされる時、新たな代表元は元の連結成分の代表元のうちどちらかになります。<br>
 */
class DSU
{
    private int $n;
    private array $parent;

    /**
     * DSU constructor.<br>
     * n 頂点 00 辺の無向グラフを作ります。<br>
     * 制約<br>
     * 0 ≤ n ≤ 10^8 <br>
     * 計算量<br>
     * O(n)<br>
     * @param int $n
     */
    function __construct(int $n)
    {
        $this->n = $n;
        $this->parent = array_fill(0,$n,-1);
    }

    /**
     * 辺 (a, b) を足します。<br>
     * a, bが連結だった場合はその代表元、非連結だった場合は新たな代表元を返します。<br>
     * 制約<br>
     * 0 ≤ a < n<br>
     * 0 ≤ b < n<br>
     * 計算量<br>
     * ならし O(α(n))
     * @param int $a
     * @param int $b
     * @return int
     */
    public function merge(int $a, int $b):int
    {
        assert(0 <= $a && $a < $this->n);
        assert(0 <= $b && $b < $this->n);
        $x = $this->leader($a);
        $y = $this->leader($b);
        if($x === $y){
            return $x;
        }
        if(-$this->parent[$x] < -$this->parent[$y]){
            [$x, $y] = [$y, $x];
        }
        $this->parent[$x] += $this->parent[$y];
        $this->parent[$y] = $x;
        return $x;
    }

    /**
     * 頂点 a, ba,b が連結かどうかを返します。<br>
     * 制約<br>
     * 0 ≤ a < n<br>
     * 0 ≤ b < n<br>
     * 計算量<br>
     * ならし O(α(n))
     * @param int $a
     * @param int $b
     * @return bool
     */
    public function same(int $a, int $b): bool
    {
        assert(0 <= $a && $a < $this->n);
        assert(0 <= $b && $b < $this->n);
        return $this->leader($a) === $this->leader($b);
    }

    /**
     * 頂点aの属する連結成分の代表元を返します。<br>
     * 制約<br>
     * 0 ≤ a < n<br>
     * 計算量<br>
     * ならし O(α(n))<br>
     * @param int $a
     * @return int
     */
    public function leader(int $a): int
    {
        if($this->parent[$a] < 0){
            return $a;
        }
        return $this->parent[$a] = $this->leader($this->parent[$a]);
    }

    /**
     * 頂点aの属する連結成分のサイズを返します。<br>
     * 制約<br>
     * 0 ≤ a < n<br>
     * 計算量<br>
     * ならし O(α(n))<br>
     * @param int $a
     * @return int
     */
    public function size(int $a):int
    {
        assert(0 <= $a && $a < $this->n);
        return -$this->parent[$this->leader($a)];
    }

    /**
     * グラフを連結成分に分け、その情報を返します。<br>
     * 返り値は「「一つの連結成分の頂点番号のリスト」のリスト」です。<br>
     * (内側外側限らず)vector内でどの順番で頂点が格納されているかは未定義です。<br>
     * 計算量<br>
     * O(n)<br>
     * @return array
     */
    public function groups(): array
    {
        $leader_buf = $group_size = array_fill(0,$this->n, 0);
        $result = [];
        for($i=0;$i<$this->n;$i++){
            $result[$this->leader($i)][] = $i;
        }
        return $result;
    }
}

[$N, $Q] = fscanf(STDIN, "%d%d");
$dsu = new DSU($N);
while($Q--){
    [$T, $U, $V] = fscanf(STDIN, "%d%d%d");
    if($T === 0){
        $dsu->merge($U, $V);
    }else{
        if($dsu->same($U, $V)){
            echo 1 .PHP_EOL;
        }else{
            echo 0 .PHP_EOL;
        }
    }
}