Language: C++

#!/usr/bin/env python3
import sys


class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n+1)]
        self.rank = [0] * (n+1)

    # 検索
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # 併合
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
        if self.rank[x] == self.rank[y]:
            self.rank[x] += 1

    def same_check(self, x, y):
        return self.find(x) == self.find(y)

    def get_uniq_par(self):
        return list(set(self.par))

def solve(N: int, M: int, X: "List[int]", Y: "List[int]", Z: "List[int]"):
    uni = UnionFind(N)

    for x,y in zip(X,Y):
        uni.union(x-1,y-1)

    print(len(uni.get_uniq_par())-1)
    
    return


# Generated by 1.1.4 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    N = int(next(tokens))  # type: int
    M = int(next(tokens))  # type: int
    X = [int()] * (M)  # type: "List[int]" 
    Y = [int()] * (M)  # type: "List[int]" 
    Z = [int()] * (M)  # type: "List[int]" 
    for i in range(M):
        X[i] = int(next(tokens))
        Y[i] = int(next(tokens))
        Z[i] = int(next(tokens))
    solve(N, M, X, Y, Z)

if __name__ == '__main__':
    main()


Compiler output:
a.cc:1:2: error: invalid preprocessing directive #!
    1 | #!/usr/bin/env python3
      |  ^
a.cc:10:7: error: invalid preprocessing directive #\U0000691c\U00007d22
   10 |     # 検索
      |       ^~~~
a.cc:18:7: error: invalid preprocessing directive #\U00004f75\U00005408
   18 |     # 併合
      |       ^~~~
a.cc:46:3: error: invalid preprocessing directive #Generated
   46 | # Generated by 1.1.4 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
      |   ^~~~~~~~~
a.cc:53:28: error: stray '#' in program
   53 |     N = int(next(tokens))  # type: int
      |                            ^
a.cc:54:28: error: stray '#' in program
   54 |     M = int(next(tokens))  # type: int
      |                            ^
a.cc:55:24: error: stray '#' in program
   55 |     X = [int()] * (M)  # type: "List[int]"
      |                        ^
a.cc:56:24: error: stray '#' in program
   56 |     Y = [int()] * (M)  # type: "List[int]"
      |                        ^
a.cc:57:24: error: stray '#' in program
   57 |     Z = [int()] * (M)  # type: "List[int]"
      |                        ^
a.cc:64:16: warning: multi-character literal with 8 characters exceeds 'int' size of 4 bytes
   64 | if __name__ == '__main__':
      |                ^~~~~~~~~~
a.cc:2:1: error: 'import' does not name a type
    2 | import sys
      | ^~~~~~
a.cc:2:1: note: C++20 'import' only available with '-fmodules-ts'