Problem:


Score : 300 points

Problem Statement

There are N cities numbered 1 through N, and M bidirectional roads numbered 1 through M.
Road i connects City A_i and City B_i.

Snuke can perform the following operation zero or more times:

Choose two distinct cities that are not directly connected by a road, and build a new road between the two cities.

After he finishes the operations, it must be possible to travel from any city to any other cities by following roads (possibly multiple times).

What is the minimum number of roads he must build to achieve the goal?

Constraints

2 \leq N \leq 100,000

1 \leq M \leq 100,000

1 \leq A_i < B_i \leq N

No two roads connect the same pair of cities.

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N M
A_1 B_1
:
A_M B_M

Output

Print the answer.

Sample Input 1

3 1
1 2

Sample Output 1

1

Initially, there are three cities, and there is a road between City 1 and City 2.

Snuke can achieve the goal by building one new road, for example, between City 1 and City 3.
After that,

We can travel between 1 and 2 directly.

We can travel between 1 and 3 directly.

We can travel between 2 and 3 by following both roads (2 - 1 - 3).



Sample input:
3 1
1 2


Sample output:
1

Submission status: Accepted

Solution code:
#!/usr/bin/env python3

import sys
import math
from bisect import bisect_right as br
from bisect import bisect_left as bl
sys.setrecursionlimit(2147483647)
from heapq import heappush, heappop,heappushpop
from collections import defaultdict
from itertools import accumulate
from collections import Counter
from collections import deque
from operator import itemgetter
from itertools import permutations
mod = 10**9 + 7
inf = float('inf')
def I(): return int(sys.stdin.readline())
def LI(): return list(map(int,sys.stdin.readline().split()))


def root(x):
    if par[x] == x:
        return x
    par[x] = root(par[x])
    return par[x]

def union(x,y):
    x = root(x)
    y = root(y)
    if rank[x] < rank[y]:
        par[x] = y
    else:
        par[y] = x
        if rank[x] == rank[y]:
            rank[x] += 1


n, m = LI()
par = [i for i in range(n)]
rank = [0] * n
for _ in range(m):
    a, b = LI()
    if root(a-1) != root(b-1):
        union(a-1, b-1)

s = set()
for i in range(n):
    s.add(root(i))
ans = len(s)-1
print(ans)

Submission status: Accepted

Solution code:
#入力:N,M(int:整数)
def input2():
	return map(int,input().split())

#入力:[n1,n2,...nk](int:整数配列)
def input_array():
	return list(map(int,input().split()))

class UnionFind():
	"""docstring for UnionFind"""
	def __init__(self, n):
		self.parents = [-1]*n

	def find(self,x):
		if self.parents[x] < 0: #自分が親である時
			return x
		else: #自分が子供である時
			self.parents[x]=self.find(self.parents[x])
			return self.parents[x]

	def union(self,x,y):
		# 各要素の親要素を返す
		x=self.find(x)
		y=self.find(y)

		if x==y:
			return
		if self.parents[x] > self.parents[y]:
			x,y=y,x

		self.parents[x]+=self.parents[y]
		self.parents[y]=x
		# print(self.parents)
n,m=input2()
AB=[input_array() for _ in range(m)]


uf = UnionFind(n)
for ab in AB:
	a=ab[0]
	b=ab[1]
	uf.union(a-1,b-1)

ans=0
for i in uf.parents:
	if i < 0:
		ans+=1
print(ans-1)

Submission status: Accepted

Solution code:
import sys
def input():
    return sys.stdin.readline()[:-1]

N,M=map(int,input().split())

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

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

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

uf=UnionFind(N+1)
  
for i in range(M):
  a,b=map(int, input().split())
  uf.union(a,b)

print(uf.group_count()-2)

Submission status: Accepted

Solution code:
class UnionFind:
  def __init__(self, n):
    self.g = [i for i in range(n)]
    self.n = n
  def whichclass(self, x):
    if self.g[x]==x:
      return x
    else:
      self.g[x] = self.whichclass(self.g[x])
      return self.g[x]
  def union(self, x, y):
    cx = self.whichclass(x)
    cy = self.whichclass(y)
    if cx==cy:
      return
    minc = min(cx, cy)
    maxc = max(cx, cy)
    self.g[maxc] = minc
  def __getitem__(self, x):
    return self.whichclass(x)
  
N, M = map(int, input().split())
UF = UnionFind(N)
for i in range(M):
  a, b = map(int, input().split())
  a, b = a-1, b-1
  UF.union(a, b)
count = [0 for i in range(N)]
for i in range(N):
  count[UF[i]] = 1
print(sum(count)-1)

Submission status: Accepted

Solution code:
import os
import sys
from io import BytesIO, IOBase



def main():
    n, m = getints()
    g = [0] * n
    for i in range(n):
        g[i] = i
    def find(u):
        if u == g[u]:
            return u
        else:
            g[u] = find(g[u])
            return g[u]
    def merge(u, v):
        g[find(u)] = find(v)
    for _ in range(m):
        a, b = getints()
        merge(a - 1, b - 1)
    cnt = 0
    for i in range(n):
        if i == g[i]:
            cnt += 1
    print(cnt - 1)


# region fastio


BUFSIZE = 8192


class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)


class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")


sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)


def input(): return sys.stdin.readline().rstrip("\r\n")
def getint(): return int(input())
def getints(): return list(map(int, input().split()))
def getint1(): return list(map(lambda x: int(x) - 1, input().split()))


# endregion


if __name__ == "__main__":
    main()

Submission status: Accepted

Solution code:
import sys
from collections import deque


N, M = map(int, input().split())
edge = [[] for _ in range(N)]
for s in sys.stdin.readlines():
    a, b = map(lambda x: int(x) - 1, s.split())
    edge[a].append(b)
    edge[b].append(a)

path = [-1] * N
cnt = 0
for i in range(N):
    if path[i] < 0:
        cnt += 1
        q = deque()
        path[i] = i
        q.append(i)
        while q:
            p = q.popleft()
            for np in edge[p]:
                if path[np] < 0:
                    path[np] = i
                    q.append(np)

print(cnt - 1)

Submission status: Accepted

Solution code:
from networkx import *
 
n,m = map(int, input().split())
G = Graph()
 
for i in range(m):
    a,b = map(int, input().split())
    G.add_edges_from([(a, b)])
 
m3 = list(connected_components(G))

count = 0
 
if len(max(connected_components(G), key=len)) == n:
    print(0)
    exit()
else:
    for i in m3:
        count += len(i)
 
sub = n - count
 
print((len(m3)-1)+sub)

Submission status: Accepted

Solution code:
# queueのライブラリをインポート
import queue

n,m = map(int,input().split())
# 各都市から繋がっている都市を記録
road = [[] for i in range(n)]
for i in range(m):
  a,b = map(int,input().split())
  road[a-1].append(b-1)
  road[b-1].append(a-1)
ans = 0
# まだ訪れていない都市を記録
city = set(i for i in range(n))
# まだ訪れていない都市がある間繰り返す
while len(city):
  # queueを用意する
  q = queue.Queue()
  # 初期値としてまだ訪れていない都市を入れる
  q.put(city.pop())
  # 1つのグループを全て辿る
  while not q.empty():
    x = q.get()
    for i in road[x]:
      # 既に訪れたか調べる
      if i in city:
        # 訪れていなければqueueに追加する
        q.put(i)
        # 未訪問の都市のリストから除外する
        city.discard(i)
  # まだ訪れていない都市があれば答えを増やす
  if len(city):
    ans += 1
print(ans)

Submission status: Accepted

Solution code:
N, M = map(int, input().split())

class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x
    def size(self, x):
        return -self.parents[self.find(x)]
    def same(self, x, y):
        return self.find(x) == self.find(y)
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
    def group_count(self):
        return len(self.roots())
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}
    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

uf = UnionFind(N)

for _ in range(M):
    a, b = map(int, input().split())
    uf.union(a-1, b-1)

print(len(uf.roots())-1)

Submission status: Accepted

Solution code:
import sys


class UnionFind():
    def __init__(self, n):
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x


N, M = map(int, input().split())
uf = UnionFind(N)
for _ in range(M):
    a, b = map(lambda x: int(x) - 1, sys.stdin.readline().split())
    uf.union(a, b)

print(sum(p < 0 for p in uf.parents) - 1)

Submission status: Accepted

Solution code:
class UnionFind:
    def __init__(self, n=1):
        self.parent = [i for i in range(n)]
        self.rank = [0] * n
    
    def find(self, x):
        if self.parent[x] == x:
            return x
        else:
            self.parent[x] = self.find(self.parent[x])
            return self.parent[x]
    
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                x, y = y, x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.parent[y] = x
    
    def is_same(self, x, y):
        return self.find(x) == self.find(y)


N, M = map(int, input().split())
uf = UnionFind(N)
for i in range(M):
    a, b = map(int, input().split())
    uf.union(a-1, b-1)

for i in range(N):
    uf.find(i)

print(len(set([uf.find(i) for i in range(N)])) - 1)

Submission status: Accepted

Solution code:
class UnionFind():
    def __init__(self, n):
        self.parents = list(range(n))
        self.rank = [0] * n

    def find(self, x):
        if self.parents[x] == x:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.rank[x] < self.rank[y]:
            self.parents[x] = y
        else:
            self.parents[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

n, m = map(int, input().split())

uf = UnionFind(n)

for _ in range(m):
    x, y = map(int, input().split())
    x, y = x-1, y-1
    uf.union(x, y)

for i in range(n):
    uf.find(i)

print(len(set(uf.parents))-1)

Submission status: Accepted

Solution code:
n,m = map(int,input().split())
par = [i for i in range(n+1)]
def find(x):
    if par[x] == x:
        return x
    else:
        par[x] = find(par[x]) 
        return par[x]
def same(x,y):
    return find(x) == find(y)
def unite(x,y):
    x = find(x)
    y = find(y)
    if x == y:
        return 0
    par[max(x,y)] = min(x,y)



for _ in range(m):
    a,b=map(int,input().split())
    unite(a,b)

tag=[0 for i in range(n+1)]
for i in range(1,n+1):
    tag[find(i)]=1
print(sum(tag)-1)

Submission status: Accepted

Solution code:
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

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

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())


N, M = map(int, input().split())
uf = UnionFind(N)

for _ in range(M):
    A, B = map(int, input().split())
    uf.union(A - 1, B - 1)

roots = uf.roots()
print(len(roots)-1)

Submission status: Accepted

Solution code:
#self.r[x] means root of "x" if "x" isn't root, else number of elements
class UnionFind():
    def __init__(self, n):
        self.r = [-1 for i in range(n)]
    
    #use in add-method
    def root(self, x):
        if self.r[x] < 0: #"x" is root
            return x
        self.r[x] = self.root(self.r[x])
        return self.r[x]

    #add new path
    def add(self, x, y):
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        self.r[x] += self.r[y]
        self.r[y] = x
        return True

n, m = map(int, input().split())
UF = UnionFind(n)
for i in range(m):
    a, b = map(lambda x: int(x)-1, input().split())
    UF.add(a, b)

ans = 0
for i in UF.r:
    if i < 0:
        ans += 1

print(ans-1)

Submission status: Accepted

Solution code:

import sys
sys.setrecursionlimit(10000)

class UnionFindTree:
	def __init__(self,size):
		"""
		size個の集合を作成
		負の値は、ルートで集合の個数
		正の値は、次の要素
		"""
		self.table=[-1 for _ in range(size)]

	def find(self,x):
		"""
		グループ番号を取得
		"""
		# 本質的には、木を上に辿るだけ
		# ただし、通常、UnionFindでは経路圧縮を行うのが普通らしい
		if self.table[x] < 0:
			# 一個上がルートならば、それを返して終わり
			return x
		else:
			# そうでなければ、ルートまで辿るついでに経路圧縮
			self.table[x] = self.find(self.table[x])
			return self.table[x]
		return x

	# 併合
	def union(self,x,y):
		# s1,s2は正
		# table[s1],table[s2]は負
		s1=self.find(x)
		s2=self.find(y)
		if s1!=s2:
			# 個数が小さい方をルートにくっつける
			# ルートサイズは負なので、ルートが大きい方が個数が小さい?
			if self.table[s2]<=self.table[s1]:
				# s1が小さい
				self.table[s1]+=self.table[s2]
				self.table[s2]=s1
			else:
				# s2が小さい
				self.table[s2]+=self.table[s1]
				self.table[s1]=s2
			return True
		return False

	def groupCount(self):
		ret=0
		for i in range(len(self.table)):
			if self.table[i] < 0:
				ret+=1
		return ret

N,M=map(int,input().split())

UF=UnionFindTree(N)

for _ in range(M):
	A,B=map(int,input().split())
	UF.union(A-1,B-1)


print(UF.groupCount()-1)

Submission status: Accepted

Solution code:
n,m = map(int, input().split())
#a = list(map(int, input().split()))

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

    def root(self,x):
        while self.par[x]!=x:
            self.par[x]=self.par[self.par[x]]
            x=self.par[x]
        return x

    def unite(self,x,y):
        x=self.root(x)
        y=self.root(y)
        if x==y:
            return False
        if self.siz[x]<self.siz[y]:
            x,y=y,x
        self.siz[x]+=self.siz[y]
        self.par[y]=x
        return True

    def is_same(self,x,y):
        return self.root(x)==self.root(y)

    def size(self,x):
        return self.siz[self.root(x)]

uni = UnionFind(n)
for i in range(m):
    a,b = map(int, input().split())
    uni.unite(a-1, b-1)

cnt = [0]*(n)
for i in range(n):
    cnt[uni.root(i)] = 1

print(sum(cnt)-1)

Submission status: Accepted

Solution code:
n,m=map(int,input().split())
par=[-1]*n

# this par & rank are initialized lists
def find(x):
    global par
    if par[x]<0:
        return x
    else:
        par[x]=find(par[x])
        return par[x]
        
def unite(x,y):
    x,y=find(x),find(y)
    global par
    if x==y:return False
    else:
        if par[x]>par[y]:x,y=y,x
        par[x]+=par[y]
        par[y]=x
        return True

def size(x):
    return -par[find(x)]
def same(x,y):
    return find(x)==find(y)

for i in range(m):
  a,b=map(int,input().split())
  a-=1
  b-=1
  unite(a,b)

cnt=0
for i in par:
  if i<0:
    cnt+=1
print(cnt-1)

Submission status: Accepted

Solution code:
class Unionfind:
     
    def __init__(self,n):
        self.uf = [-1]*n
 
    def find(self,x):
        if self.uf[x] < 0:
            return x
        else:
            self.uf[x] = self.find(self.uf[x])
            return self.uf[x]
 
    def same(self,x,y):
        return self.find(x) == self.find(y)
 
    def union(self,x,y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return False
        if self.uf[x] > self.uf[y]:
            x,y = y,x
        self.uf[x] += self.uf[y]
        self.uf[y] = x
        return True
 
    def size(self,x):
        x = self.find(x)
        return -self.uf[x]

n,m = map(int,input().split())

u = Unionfind(n)
count = 0
for i in range(m):
    a,b = map(int,input().split())
    a -= 1
    b -= 1
    u.union(a,b)

s = set()
for i in range(n):
    s.add(u.find(i))
print(len(s)-1)

Submission status: Accepted

Solution code:
class UnionFind:
    pairs = {}
    def __init__(self, n):
        for i in range(n):
            self.pairs[i] = i

    def set(self, s, t):
        if s in self.pairs and self.pairs[s] == s:
            self.pairs[s] = self.root(t)
        else:
            self.pairs[t] = self.root(s)

    def root(self, x):
        if self.pairs[x] == x:
            return x
        self.pairs[x] = self.root(self.pairs[x])
        return self.pairs[x]

    def unite(self, x, y):
        rx = self.root(x)
        ry = self.root(y)
        if rx == ry :
            return
        self.pairs[rx] = ry
    def same(self, x, y ):
        rx = self.root(x)
        ry = self.root(y)
        if rx == ry:
            return True
        else:
            return False
        
def main():
    n, m = map(int, input().split())

    uf = UnionFind(n)
    pairs =[]
    for i in range(m):
        s, t = map(int,input().split())
        if uf.same(s-1, t-1) == False:
            uf.unite(s-1,t-1)
        #pairs.append([s-1,t-1])

    # pairs.sort()
    # for pair in pairs:
    #     uf.set(pair[0], pair[1])
    #
    # for pair in pairs:
    #     uf.set(pair[0], pair[1])

    root = []
    for i in range(n):
        root.append(uf.root(i))
 #       print(i, uf.root(i))
    single_root = set(root)

    print(len(single_root)-1)
main()

Submission status: Accepted

Solution code:
class UnionFind():
    # 作りたい要素数nで初期化
    # 使用するインスタンス変数の初期化
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
    # ノードxのrootノードを見つける
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def unite(self, x, y):
    # 木の併合、入力は併合したい各ノード⇒(a,b)
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.parents[x] > self.parents[y]:
            x, y = y, x
        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
    # ノードxが属する木のサイズを返す
        return -self.parents[self.find(x)]

    def same(self, x, y):
    # 入力ノード(x,y)が同じグループに属するかを返す
        return self.find(x) == self.find(y)

    def members(self, x):
    #ノードxが属するメンバーをリスト形式で返す
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
    #親全てをリスト形式で返す
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
    #グループ数の合計を返す
        return len(self.roots())

import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.readline

n,m=map(int,input().split())
u=UnionFind(n)
for i in range(m):
    a,b=map(int,input().split())
    a-=1
    b-=1
    u.unite(a,b)
ans=u.roots()
print(len(ans)-1)

Submission status: Accepted

Solution code:
import os
import sys
from io import BytesIO, IOBase



def main():
    sys.setrecursionlimit(10**6 + 10)
    n, m = getints()
    g = [0] * n
    for i in range(n):
        g[i] = i
    def find(u):
        if u == g[u]:
            return u
        else:
            g[u] = find(g[u])
            return g[u]
    def merge(u, v):
        g[find(u)] = find(v)
    for _ in range(m):
        a, b = getints()
        merge(a - 1, b - 1)
    cnt = 0
    for i in range(n):
        if i == g[i]:
            cnt += 1
    print(cnt - 1)


# region fastio


BUFSIZE = 8192


class FastIO(IOBase):
    newlines = 0

    def __init__(self, file):
        self._fd = file.fileno()
        self.buffer = BytesIO()
        self.writable = "x" in file.mode or "r" not in file.mode
        self.write = self.buffer.write if self.writable else None

    def read(self):
        while True:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            if not b:
                break
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines = 0
        return self.buffer.read()

    def readline(self):
        while self.newlines == 0:
            b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
            self.newlines = b.count(b"\n") + (not b)
            ptr = self.buffer.tell()
            self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr)
        self.newlines -= 1
        return self.buffer.readline()

    def flush(self):
        if self.writable:
            os.write(self._fd, self.buffer.getvalue())
            self.buffer.truncate(0), self.buffer.seek(0)


class IOWrapper(IOBase):
    def __init__(self, file):
        self.buffer = FastIO(file)
        self.flush = self.buffer.flush
        self.writable = self.buffer.writable
        self.write = lambda s: self.buffer.write(s.encode("ascii"))
        self.read = lambda: self.buffer.read().decode("ascii")
        self.readline = lambda: self.buffer.readline().decode("ascii")


sys.stdin, sys.stdout = IOWrapper(sys.stdin), IOWrapper(sys.stdout)


def input(): return sys.stdin.readline().rstrip("\r\n")
def getint(): return int(input())
def getints(): return list(map(int, input().split()))
def getint1(): return list(map(lambda x: int(x) - 1, input().split()))


# endregion


if __name__ == "__main__":
    main()

Submission status: Accepted

Solution code:
import sys

sys.setrecursionlimit(10**7)
def MI(): return map(int,sys.stdin.readline().rstrip().split())


class UnionFind:
    def __init__(self,n):
        self.par = [i for i in range(n+1)]  # 親のノード番号
        self.rank = [0]*(n+1)

    def find(self,x):  # xの根のノード番号
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    def same_check(self,x,y):  # x,yが同じグループか否か
        return self.find(x) == self.find(y)

    def unite(self,x,y):  # x,yの属するグループの併合
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            x,y = y,x
        if self.rank[x] == self.rank[y]:
            self.rank[x] += 1
        self.par[y] = x


N,M = MI()
UF = UnionFind(N)
for _ in range(M):
    a,b = MI()
    UF.unite(a,b)

A = UF.par
A = [UF.find(A[i]) for i in range(1,N+1)]

print(len(set(A))-1)

Submission status: Accepted

Solution code:
import math,itertools,fractions,heapq,collections,bisect,sys,queue,copy

sys.setrecursionlimit(10**7)
inf=10**20
mod=10**9+7
dd=[(-1,0),(0,1),(1,0),(0,-1)]
ddn=[(-1,0),(-1,1),(0,1),(1,1),(1,0),(1,-1),(0,-1),(-1,-1)]

def LI(): return [int(x) for x in sys.stdin.readline().split()]
# def LF(): return [float(x) for x in sys.stdin.readline().split()]
def I(): return int(sys.stdin.readline())
def F(): return float(sys.stdin.readline())
def LS(): return sys.stdin.readline().split()
def S(): return input()

# Union-Find -- START --
class UnionFind():
  def __init__(self,sz):
    self.sz=sz
    self.data=[-1]*sz

  def unite(self,x,y):
    x=self.find(x)
    y=self.find(y)
    if x==y:
      return False
    if self.data[x]>self.data[y]:
      x,y=y,x
    self.data[x]+=self.data[y]
    self.data[y]=x
    return True

  def find(self,k):
    if self.data[k]<0:
      return k
    self.data[k]=self.find(self.data[k])
    return self.data[k]

  def size(self,k):
    return -self.data[self.find(k)]
# Union-Find --- END ---

def main():
  n,m=LI()
  uf=UnionFind(n)
  for _ in range(m):
    a,b=LI()
    a-=1
    b-=1
    uf.unite(a,b)
  
  d={}
  for i in range(n):
    d[uf.find(i)]=i

  return len(d)-1

# main()
print(main())

Submission status: Accepted

Solution code:
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.par = [i for i in range(n+1)]  # �?要�?の親要�?の番号を�?�納するリス�?
        self.rank = [0] * (n+1)
        self.size = [1] * (n+1)  # 根?��ルート）�?�場合、そのグループ�?�要�?数を�?�納するリス�?

    # 要�? x が属するグループ�?�根を返す
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # 要�? x が属するグループと要�? y が属するグループとを併合す�?
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x == y:
            return
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
            self.size[y] += self.size[x]
        else:
            self.par[y] = x
            self.size[x] += self.size[y]
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

    # 要�? x, y が同じグループに属するかど�?かを返す
    def same_check(self, x, y):
        return self.find(x) == self.find(y)
    
    # 要�? x が属するグループに属する要�?をリストで返す
    def members(self, x):
        root = self.find(x)
        return [i for i in range(1,self.n+1) if self.find(i) == root]

    # 要�? x が属するグループ�?�サイズ?��要�?数?��を返す
    def get_size(self, x):
        return self.size[self.find(x)]

    # すべての根の要�?をリストで返す
    def roots(self):
        return [i for i, x in enumerate(self.par) if x == i and i != 0]
    
    # グループ�?�数を返す
    def group_count(self):
        return len(self.roots())
    
    # {ルート要�?: [そ�?�グループに含まれる要�?のリス�?], ...} の辞書を返す
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

n,m = map(int,input().split())
uf = UnionFind(n)
for i in range(m):
    a,b = map(int,input().split())
    uf.union(a,b)
print(len(uf.roots())-1)

Submission status: Accepted

Solution code:
class UnionFind():
    def __init__(self, n):
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x


N,M=map(int,input().split())
A=[0]*M
B=[0]*M
for i in range(M):
    A[i],B[i]=map(int,input().split())

uf=UnionFind(N+1)
for i in range(M):
    uf.union(A[i],B[i])
a=[0]*(N+1)
con=0

for i in uf.parents:
    if(i<0):
        con=con+1

print(con-2)

Submission status: Accepted

Solution code:
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

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

    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N, M = map(int, input().split())
AB = [list(map(int, input().split())) for _ in range(M)]

uf = UnionFind(N+1)
for ab in AB:
    uf.union(ab[0], ab[1])

print(uf.group_count() - 1 - 1) # [0]を除いたグループの数 - 1