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 <= N <= 100,000
- 1 <= M <= 100,000
- 1 <= A_i < B_i <= 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, between 1 and 3 directly, and between 2 and 3 by following both roads (2 - 1 - 3).

# Buggy version:
n,m=map(int,input().split())
g=[[],]*n
for i in range(m):
	u,v=map(int,input().split())
	u-=1
	v-=1
	g[u].append(v)
	g[v].append(u)

vis=[False]*n
stk=[]

cnt=0
for i in range(n):
	if not vis[i]:
		cnt+=1
		vis[i]=True
		stk.append(i)
	while len(stk)!=0:
		u=stk[-1]
		stk.pop()
		if vis[u]:
			continue
		for v in g[u]:
			if not vis[v]:
				vis[v]=True
				stk.append(v)
print(cnt-1)


# Fixed version:
n,m=map(int,input().split())
g=[[] for i in range(n)]
for i in range(m):
	u,v=map(int,input().split())
	u-=1
	v-=1
	g[u].append(v)
	g[v].append(u)

vis=[False]*n
stk=[]

cnt=0
tt=0
for i in range(n):
	if not vis[i]:
		cnt+=1
		stk.append(i)
	while len(stk)!=0:
		u=stk[-1]
		stk.pop()
		if vis[u]:
			continue
		vis[u]=True
		for v in g[u]:
			tt+=1
			if not vis[v]:
				stk.append(v)
print(cnt-1)

# Buggy version:
from sys import setrecursionlimit, stdin
import numpy as np

readline = stdin.readline
setrecursionlimit(10 ** 6)

def find(parent, i):
    t = parent[i]
    if t < 0:
        return i
    t = find(parent, t)
    parent[i] = t
    return t
  
def unite(parent, i, j):
    i = find(parent, i)
    j = find(parent, j)
    if i == j:
        return
    parent[j] += parent[i]
    parent[i] = j


N,M = map(int,readline().split())
parent = - np.ones(N,int)

for _ in range(M):
    A, B = map(lambda x: int(x) - 1, readline().split())
    unite(parent, A, B)
ans = 0
print(parent)
for i in range(N):
    if parent[i] < 0:
        ans += 1
    else:
        print(parent[i])
print(ans-1)

# Fixed version:
from sys import setrecursionlimit, stdin
import numpy as np

readline = stdin.readline
setrecursionlimit(10 ** 6)

def find(parent, i):
    t = parent[i]
    if t < 0:
        return i
    t = find(parent, t)
    parent[i] = t
    return t
  
def unite(parent, i, j):
    i = find(parent, i)
    j = find(parent, j)
    if i == j:
        return
    parent[j] += parent[i]
    parent[i] = j

N,M = map(int,readline().split())
parent = - np.ones(N,int)

for _ in range(M):
    A, B = map(lambda x: int(x) - 1, readline().split())
    unite(parent, A, B)

ans = 0
for i in range(N):
    if parent[i] < 0:
        ans += 1
print(ans-1)

# Buggy version:
n,m=map(int, input().split())

roads=[]

for _ in range(m):
  a,b=map(int, input().split())
  roads.append([a,b].sort())
  
group=[i for i in range(n)]

group2=[]

while group2!=group:
  group2=group
  for road in roads:
    group[road[1]-1]=group[road[0]-1]

counter=1
for i in range(n-1):
  if group[i]!=group[i+1]:
    counter+=1
    
print(counter-1) 
      

# Fixed version:
n,m=map(int, input().split())

roads=[]

for _ in range(m):
  a,b=map(int, input().split())
  roads.append([a,b])
  
group=[i for i in range(n)]

group2=[]

while group2!=group:
  group2=list(group)
  for road in roads:
    if group[road[1]-1]>group[road[0]-1]:
      group[road[1]-1]=group[road[0]-1]
    else:
      group[road[0]-1]=group[road[1]-1]
  
counter=1
group.sort()

for i in range(n-1):
  if group[i]!=group[i+1]:
    counter+=1

print(counter-1)

# Buggy version:
f = lambda x: x if p[x]<0 else f(p[x])
N,M = map(int,input().split())
p = [-1]*N
for _ in range(M):
  A,B = map(lambda x:f(int(x)-1),input().split())
  if A==B: continue
  elif A<B: A,B=B,A
  p[A] += p[B]
  p[B] = A
  print(p)
print(sum(i<0 for i in p)-1)

# Fixed version:
f = lambda x: x if p[x]<0 else f(p[x])
N,M = map(int,input().split())
p = [-1]*N
for _ in range(M):
  A,B = map(lambda x:f(int(x)-1),input().split())
  if A==B: continue
  elif A<B: A,B=B,A
  p[A] += p[B]
  p[B] = A
print(sum(i<0 for i in p)-1)

# Buggy version:
class UnionFind:
  def __init__(self, n):
    self.n = [-1]*n
    self.r = [0]*n
    self.co = n

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

  def unite(self, x, y):
    x = self.find(x)
    y = self.find(y)
    if x == y:
      return
    if self.r[x] > self.r[y]:
      x, y = y, x
    self.n[y] += self.n[x]
    self.n[x] = y
    if self.r[x] == self.r[y]:
      self.r[y] += 1
    self.co -= 1

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

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

  def set_count(self):
    return self.co
  
n, m = map(int, input().split())
uf = UnionFind(n)
for _ in range(m):
  a, b = map(int, input().split())
  uf.unite(a-1, b-1)
print(uf.set_count()-1)

# Fixed version:
class UnionFind:
  def __init__(self, n):
    self.n = [-1]*n
    self.r = [0]*n
    self.co = n

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

  def unite(self, x, y):
    x = self.find_root(x)
    y = self.find_root(y)
    if x == y:
      return
    elif self.r[x] > self.r[y]:
      self.n[x] += self.n[y]
      self.n[y] = x
    else:
      self.n[y] += self.n[x]
      self.n[x] = y
      if self.r[x] == self.r[y]:
        self.r[y] += 1
    self.co -= 1

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

  def size(self, x):
    return -self.n[self.find_root(x)]

  def set_count(self):
    return self.co

n, m = map(int, input().split())
uf = UnionFind(n)
for _ in range(m):
  a, b = map(int, input().split())
  uf.unite(a-1, b-1)
print(uf.set_count()-1)

# Buggy version:
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 roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
      
    def group_count(self):
        return len(self.roots)

n, m = map(int, input().split())
un = UnionFind(n)
for _ in range(m):
    a, b = map(int, input().split())
    un.union(a, b)
print(un.group_count() - 1)


# Fixed version:
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 roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]

    def group_count(self):
        return len(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-1, b-1)
print(uf.group_count() - 1)

# Buggy version:
from collections import deque
n,m = map(int,input().split())
g = [[] for _ in range(n)]
for i in range(m):
    a, b = map(int,input().split())
    g[a-1].append(b-1)
    g[b-1].append(a-1)

checked = [False]*n
q = deque([])
cnt = 0

#連結成分のカウント

for i in range(n):
    if checked[i]:
        continue
    checked[i] = True
    q.append(i)
    while q:
        v = q.pop()
        s = g[v]
        for j in s:
            if checked[j]:
                continue
            checked[j] = True
            q.append(j)
    cnt += 1

print(cnt - 1

# Fixed version:
from collections import deque
n,m = map(int,input().split())
g = [[] for _ in range(n)]
for i in range(m):
    a, b = map(int,input().split())
    g[a-1].append(b-1)
    g[b-1].append(a-1)

dist = [-1]*n
q = deque([])
cnt = 0

#連結成分のカウント

for i in range(n):
    if dist[i] != -1:
        continue
    dist[i] = 0
    q.append(i)
    while q:
        v = q.pop()
        s = g[v]
        for j in s:
            if dist[j] != -1:
                continue
            dist[j] = 0
            q.append(j)
    cnt += 1

print(cnt - 1)

# Buggy version:
n,m = map(int,input().split())

ans = 0

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

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

    def Unite(self,x,y):
        x = self.Root_Find(x)
        y = self.Root_Find(y)
        if x == y:
            return
        self.root[y] = x

tree = UnionFind(n)

for i in range(m):
    a,b = map(int,input().split())
    tree.Unite(a,b)
    
ans = len(list(set(tree.root))) - 1
print(ans)

# Fixed version:
n,m = map(int,input().split())

ans = 0

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

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

    def Unite(self,x,y):
        x = self.Root_Find(x)
        y = self.Root_Find(y)
        if x == y:
            return
        self.root[y] = x

tree = UnionFind(n)

for i in range(m):
    a,b = map(int,input().split())
    tree.Unite(a,b)

for i in range(n+1):
    tree.Root_Find(i)

ans = len(list(set(tree.root))) - 2
print(ans)

# Buggy version:
from collections import deque
n,m=map(int,input().split())
road=[[] for i i range(n)]
flag=[0]*n
for i in range(m):
    a,b=map(int,input().split())
    road[a-1].append(b-1)
    road[b-1].append(a-1)
count=0
for i in range(n):
    if flag[i]!=0:
        continue
    count+=1
    q=[k]
    while q:
        p=q.popleft()
        for z in road[p]:
            if flag[z]==0:
                q.append(z)
print(count-1)

# Fixed version:
from collections import deque
n,m=map(int,input().split())
road=[[] for i in range(n)]
flag=[0]*n
for i in range(m):
    a,b=map(int,input().split())
    road[a-1].append(b-1)
    road[b-1].append(a-1)
count=0
for i in range(n):
    if flag[i]!=0:
        continue
    count+=1
    q=deque([i])
    while q:
        p=q.popleft()
        flag[p]=1
        for z in road[p]:
            if flag[z]==0:
                q.append(z)
print(count-1)

# Buggy version:
#入力: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
        
n,m=input2()
AB=[input_array() for _ in range(m)]
D=[[0]*n for _ in range(n)]

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

ans=0

print(ans-1)


# Fixed version:
#入力: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)

# Buggy version:
class UnionFind:
  def __init__(self, n):
    self.class = [i for i in range(n)]
    self.n = n
  def whichclass(self, x):
    if self.class[x]==x:
      return x
    else:
      self.class[x] = self.whichclass(self.class[x])
      return self.class[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.class[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)

    

# Fixed version:
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)

# Buggy version:
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)


# Fixed version:
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)

# Buggy version:
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)
print(n+1-count)

# Fixed version:
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

if sub != 0:
    print((len(m3)-1)+sub)

else:
    print(len(m3)+sub-1)

# Buggy version:
import sys
sys.setrecursionlimit(10**9)
n,m = list(map(int,input().split()))

d = {}
for i in range(m):
    fr, to = list(map(int,input().split()))
    d[fr] = d.get(fr, []) + [to]
    d[to] = d.get(to, []) + [fr]
    
visited = [0 for i in range(n+1)]

def connect(node, i):
    visited[node] = i
    
    if node in d:
        for neig in d[node]:
            if visited[neig] == 0:
                connect(neig, i)
ans = 1
for key in range(1,n+1):
    if visited[key] == 0:
        connect(key, ans)
        ans += 1

print(ans-1)



# Fixed version:
import sys
sys.setrecursionlimit(10**9)
n,m = list(map(int,input().split()))

d = {}
for i in range(m):
    fr, to = list(map(int,input().split()))
    d[fr] = d.get(fr, []) + [to]
    d[to] = d.get(to, []) + [fr]
    
visited = [0 for i in range(n+1)]

def connect(node, i):
    visited[node] = i
    
    if node in d:
        for neig in d[node]:
            if visited[neig] == 0:
                connect(neig, i)
ans = 1
for key in range(1,n+1):
    if visited[key] == 0:
        connect(key, ans)
        ans += 1

print(max(visited)-1)

# Buggy version:
N, M = map(int, input().split())
ABs = [tuple(map(int, input().split())) for _ in range(M)]

par = [i for i in range(N)]

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:
        par[x] = y

for a, b in ABs:
    unite(a-1, b-1)

print(len(set(par) -1))


# Fixed version:
N, M = map(int, input().split())
ABs = [tuple(map(int, input().split())) for _ in range(M)]

par = [i for i in range(N)]

def find(x):
    if par[x] == x:
        return x
    else:
        par[x] = find(par[x])
        return par[x]

def unite(x, y):
    x = find(x)
    y = find(y)
    if x != y:
        par[x] = y

for a, b in ABs:
    unite(a-1, b-1)

for x in range(N):
    find(x)

print(len(set(par))-1)

# Buggy version:
from collections import deque
from collections import defaultdict
n, m = map(int, input().split())
g = defaultdict(list)
s = set(list(range(1, n+1)))
ans = 0
for i in range(m):
    a, b = map(int, input().split())
    g[a].append(b)
while s:
  	ans += 1
    root = s.pop()
    q = deque()
    q.append(root)
    done = set()
    done.add(root)
    while q:
        node = q.popleft()
        for adj in g[node]:
            if adj not in done:
                done.add(adj)
                q.append(adj)
                s.remove(adj)
print(ans - 1)


# Fixed version:
from collections import deque
from collections import defaultdict

n, m = map(int, input().split())
g = defaultdict(list)
s = set(list(range(1, n+1)))
ans = 0

for i in range(m):
    a, b = map(int, input().split())
    g[a].append(b)
    g[b].append(a)

while s:
    root = s.pop()
    q = deque()
    q.append(root)
    done = set()
    done.add(root)

    while q:
        node = q.popleft()

        for adj in g[node]:
            if adj not in done:
                done.add(adj)
                q.append(adj)
                s.remove(adj)
    ans += 1

print(ans - 1)

# Buggy version:
class UnionFind:
    __slots__ = ["_data_size", "_roots"]
 
    def __init__(self, data_size: int) -> None:
        self._data_size = data_size
        self._roots = [-1] * data_size
 
    def __getitem__(self, x: int) -> int:
        while self._roots[x] >= 0:
            x = self._roots[x]
        return x
 
    def is_connected(self, x: int, y: int) -> bool:
        return self[x] == self[y]
 
    def unite(self, x: int, y: int) -> None:
        x, y = self[x], self[y]
        if x == y:
            return
        if self._roots[x] > self._roots[y]:
            x, y = y, x
        self._roots[x] += self._roots[y]
        self._roots[y] = x
 
 
import sys
 
readline = sys.stdin.readline
 
N, Q = map(int, readline().split())
tree = UnionFind(N)
res = []
ans=0
for _ in range(Q):
  u, v = map(int, readline().split())
  tree.unite(u-1, v-1)
for u in range(1,N):
  print(tree.is_connected(u, 0),u)
  if not tree.is_connected(u, 0):
      ans+=1
      tree.unite(u-1, 0)
print(ans)


# Fixed version:
class UnionFind:
    __slots__ = ["_data_size", "_roots"]
 
    def __init__(self, data_size: int) -> None:
        self._data_size = data_size
        self._roots = [-1] * data_size
 
    def __getitem__(self, x: int) -> int:
        while self._roots[x] >= 0:
            x = self._roots[x]
        return x
 
    def is_connected(self, x: int, y: int) -> bool:
        return self[x] == self[y]
 
    def unite(self, x: int, y: int) -> None:
        x, y = self[x], self[y]
        if x == y:
            return
        if self._roots[x] > self._roots[y]:
            x, y = y, x
        self._roots[x] += self._roots[y]
        self._roots[y] = x
 
 
import sys
 
readline = sys.stdin.readline
 
N, Q = map(int, readline().split())
tree = UnionFind(N)
res = []
ans=0
for _ in range(Q):
  u, v = map(int, readline().split())
  tree.unite(u-1, v-1)
for u in range(1,N):
  if not tree.is_connected(u, 0):
      ans+=1
      tree.unite(u, 0)
print(ans)

# Buggy version:
class UnionFind():
    def __init__(self, n):
        self.parents = list(range(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

        self.parents[y] = x
        
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)

size = 0
for i in range(n):
    print(uf.parents[i])
    if uf.parents[i] == i:
        size += 1
print(size-1)

# Fixed version:
class UnionFind():
    def __init__(self, n):
        self.parents = list(range(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

        self.parents[y] = x
        
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)

size = 0
for i in range(n):
    if uf.parents[i] == i:
        size += 1
print(size-1)

# Buggy version:
class UnionFind:
    def __init__(self, n):
        self.r = [-1 for i in range(n)]
    
    def root(self, x):
        if self.r[x] < 0:
            return x
        self.r[x] = self.root(self.r[x])
        return self.r[x]
    
    def unite(self, x, y):
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        if self.r[x] < self.r[y]:
            temp = x
            x = y
            y = temp
        self.r[x] += self.r[y]
        self.r[y] = x
        return True
    
    def size(self, x):
        return -self.r[self.root(x)]

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.unite(a, b)

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

print(UF.r)
print(ans-1)


# Fixed version:
class UnionFind:
    def __init__(self, n):
        self.r = [-1 for i in range(n)]
    
    def root(self, x):
        if self.r[x] < 0:
            return x
        self.r[x] = self.root(self.r[x])
        return self.r[x]
    
    def unite(self, x, y):
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False
        if self.r[x] < self.r[y]:
            temp = x
            x = y
            y = temp
        self.r[x] += self.r[y]
        self.r[y] = x
        return True
    
    def size(self, x):
        return -self.r[self.root(x)]

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.unite(a, b)

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

print(ans-1)

# Buggy version:
#Union Find

#xの根を求める
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]
#xとyの属する集合の併合
def unite(x,y):
    x = find(x)
    y = find(y)
    
    if x == y:
        return False
    else:
        #sizeの大きいほうがx
        if par[x] > par[y]:
            x,y = y,x
        par[x] += par[y]
        par[y] = x
        return True

#xとyが同じ集合に属するかの判定
def same(x,y):
    return find(x) == find(y)

#xが属する集合の個数
def size(x):
    return -par[find(x)]

#初期化
#根なら-size,子なら親の頂点

n,m = map(int,input().split())
par = [-1]*n
for i in range(m):
  a,b = map(int,input().split())
  unite(a-1,b-1)
  
ans = 0

for i in range(n):
  if par[i] < 0:
    ans += 1 
print(par)
print(ans-1)

# Fixed version:
# Union Find
# xの根を求める
def find(x):
    if par[x] < 0:
        return x
    else:
        par[x] = find(par[x])
        return par[x]


# xとyの属する集合の併合
def unite(x, y):
    x = find(x)
    y = find(y)

    if x == y:
        return False
    else:
        # sizeの大きいほうがx
        if par[x] > par[y]:
            x, y = y, x
        par[x] += par[y]
        par[y] = x
        return True


# xとyが同じ集合に属するかの判定
def same(x, y):
    return find(x) == find(y)


# xが属する集合の個数
def size(x):
    return -par[find(x)]


n, m = map(int, input().split())
# 初期化
# 根なら-size,子なら親の頂点
par = [-1] * n

for _ in range(m):
    a, b = map(int, input().split())
    unite(a - 1, b - 1)
print(len(list(filter(lambda x: x < 0, par))) - 1)

# Buggy version:
n,m=list(map(int,input().split()))

par=[i for i in range(n)]
ro=dict()

for i in range(n):
    ro[i]=1
i
def root(i):
    if par[i]==i:
        return i
    else:
        a=root(par[i])
        par[i]=a
        return a

def unite(x,y):
    rx=root(x)
    ry=root(y)
    if rx!=ry:
        par[rx]=ry
        ro[ry]+=ro[rx]
        ro[rx]=0

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

from collections import Counter as cnt

ans=dict(cnt(ro.values()))

print(len(ans)-1)

# Fixed version:
n,m=list(map(int,input().split()))

par=[i for i in range(n)]
# ro=dict()
#
# for i in range(n):
#     ro[i]=1

def root(i):
    if par[i]==i:
        return i
    else:
        a=root(par[i])
        par[i]=a
        return a

def unite(x,y):
    rx=root(x)
    ry=root(y)
    if rx!=ry:
        par[rx]=ry
        # ro[ry]+=ro[rx]
        # ro[rx]=0

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

ans=set()

for i in range(n):
    rt=root(i)
    ans.add(rt)

# from collections import Counter as cnt
#
# bb=ro.values()
# ans=dict(cnt(ro.values()))

print(len(ans)-1)

# Buggy version:
N, M = map(int, input().split())
Pajew = [i for i in range(n)]
import sys
sys.setrecursionlimit(1000000)

def find(x, Pajew):
    if Pajew[x] == x:
        return x
    else:
        a = find(Pajew[x], Pajew)
        Pajew[x] =a
        return a

def unite(x, y):
    x = find(x, Pajew)
    y = find(y, Pajew)
    if x != y:
        Pajew[x] = y

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

grou = 0
for i in range(N):
    if i == Pajew[i]:
        grou +=1
        
        
print(grou-1)

# Fixed version:
N, M = map(int, input().split())
Pajew = [i for i in range(N)]
import sys
sys.setrecursionlimit(1000000)

def find(x, Pajew):
    if Pajew[x] == x:
        return x
    else:
        a = find(Pajew[x], Pajew)
        Pajew[x] =a
        return a

def unite(x, y):
    x = find(x, Pajew)
    y = find(y, Pajew)
    if x != y:
        Pajew[x] = y

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

grou = 0
for i in range(N):
    if i == Pajew[i]:
        grou +=1


print(grou-1)

# Buggy version:
class union_find():
    
    def __init__(self,n):
        self.n=n
        self.root=[-1]*(n+1)
        self.rank=[0]*(n+1)

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

    def unite(self,x,y):
        x=self.find_root(x)
        y=self.find_root(y)
        

        if x==y:
            return 
        elif self.rank[x]>self.rank[y]:
            self.root[x]+=self.root[y]
            self.root[y]=x
        else:
            self.root[y]+=self.root[x]
            self.root[x]=y

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


N,M = map(int,input().split())
g = union_find(N)
group = 0
for i in range(M):
  A,B= map(int,input().split())
  g.unite(A,B)

print(g.root)

for j in g.root:
  if j < 0:
    group += 1

print(group - 2)
  

# Fixed version:
class union_find():
    
    def __init__(self,n):
        self.n=n
        self.root=[-1]*(n+1)
        self.rank=[0]*(n+1)

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

    def unite(self,x,y):
        x=self.find_root(x)
        y=self.find_root(y)
        

        if x==y:
            return 
        elif self.rank[x]>self.rank[y]:
            self.root[x]+=self.root[y]
            self.root[y]=x
        else:
            self.root[y]+=self.root[x]
            self.root[x]=y

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


N,M = map(int,input().split())
g = union_find(N)
group = 0
for i in range(M):
  A,B= map(int,input().split())
  g.unite(A,B)

for j in g.root:
  if j < 0:
    group += 1

print(group - 2)

# Buggy version:
def find(x):
    if par[x] < 0:
        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
    if par[x] > par[y]:
        x, y = y, x
    par[x] += par[y]
    par[y] = x


N, M = map(int, input().split())
par = [-1 for i in range(N)]

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

cnt = -1
num = {0}

for i in par:
  if i > 0:
    cnt += 1

print(cnt)


# Fixed version:
def find(x):
    if par[x] < 0:
        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
    if par[x] > par[y]:
        x, y = y, x
    par[x] += par[y]
    par[y] = x


N, M = map(int, input().split())
par = [-1 for i in range(N)]

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

cnt = -1
num = {0}

for i in par:
  if i <= -1:
    cnt += 1

print(cnt)

# Buggy version:
import collections
sys.setrecursionlimit(10**6)
def dfs(visited,v,adj):
    visited[v]=True 
    for i in adj[v]:
        if not visited[i]:
            dfs(visited,i,adj)  
adj=collections.defaultdict(list)
n,m=map(int,input().split()) 
visited=[False]*n  
for i in range(m):
    u,v=map(int,input().split()) 
    adj[u-1].append(v-1)  
    adj[v-1].append(u-1) 
c=0 
for i in range(n):
    if not visited[i]:
        dfs(visited,i,adj) 
        c+=1 
print(c-1)

# Fixed version:
import collections
import sys
sys.setrecursionlimit(10**6)
def dfs(visited,v,adj):
    visited[v]=True 
    for i in adj[v]:
        if not visited[i]:
            dfs(visited,i,adj)  
adj=collections.defaultdict(list)
n,m=map(int,input().split()) 
visited=[False]*n  
for i in range(m):
    u,v=map(int,input().split()) 
    adj[u-1].append(v-1)  
    adj[v-1].append(u-1) 
c=0 
for i in range(n):
    if not visited[i]:
        dfs(visited,i,adj) 
        c+=1 
print(c-1)

# Buggy version:
import sys,io,os
I = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
I = input

n,m = map(int, I().split())
p=[-1]*(n+1)
s=[0]*(n+1)
S=n-1
def t(a):
	while p[a] != -1:
		a = p[a]
	return a
for _ in range(m):
	a,b=map(int, I().split())
	a,b = t(a), t(b)
	if a != b:
		S -= 1
		if s[a] > s[b]:
			a,b = b,a
		p[a] = b
		s[b] += s[a]
print(S)



# Fixed version:
import sys,io,os
I = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
#I = input

n,m = map(int, I().split())
p=[-1]*(n+1)
s=[0]*(n+1)
S=n-1
def t(a):
	b = a
	while p[b] != -1:
		b = p[b]
	while p[a] != -1:
		p[a],a = b,p[a]
	return a
for _ in range(m):
	a,b=map(int, I().split())
	a,b = t(a), t(b)
	if a != b:
		S -= 1
		if s[a] > s[b]:
			a,b = b,a
		p[a] = b
		s[b] += s[a]
print(S)

# Buggy version:
import sys
sys.setrecursionlimit(10 ** 9)

n, m = map(int, input().split())
root = [-1] * (110000)
def find(x):
    if root[x] < 0:
        return x
    else:
        root[x] = find(x)
        return root[x]
    

def unite(x, y):
    x = find(x)
    y = find(y)
    if x == y:
        return
    root[x] += root[y]
    root[y] = x
        

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

ans = 0
for i in range(1, n+1):
    if root[i]  < 0:
        ans += 1
print(ans-1)
print(root)

# Fixed version:
import sys
sys.setrecursionlimit(10**9)
n, m = map(int, input().split())

root = [-1]*n

def find(n):
    if root[n] < 0:
        return n
    else:
        root[n] = find(root[n])
        return root[n]

def unite(x, y):
    x = find(x)
    y = find(y)
    if x == y:
        return
    else:
        root[x] += root[y]
        root[y] = x

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

ans = -1
for i in root:
    if i < 0:
        ans += 1

print(ans)

# Buggy version:
n,m=map(int,input().split())

global fri
fri=[]
global tab
tab=[0]*n
def run(nn):
  #print(tab)
  global cnt
  if tab[nn]:
    return
  tab[nn]=1
  cnt+=1
  for i in fri[nn]:
    que.append(i)
for i in range(n):
  fri.append([])
for i in range(m):
  a,b=map(int,input().split())
  fri[a-1].append(b-1)
  fri[b-1].append(a-1)
i=0
mx=0
cc=0
while i<n:
  if tab[i]:
    i+=1
    continue
  que=[i]
  cnt=0
  while len(que)!=0:
    run(que.pop())
  mx=max(cnt,mx)
  i+=1
  cc+=1
print(cc)



# Fixed version:
n,m=map(int,input().split())

global fri
fri=[]
global tab
tab=[0]*n
def run(nn):
  #print(tab)
  global cnt
  if tab[nn]:
    return
  tab[nn]=1
  cnt+=1
  for i in fri[nn]:
    que.append(i)
for i in range(n):
  fri.append([])
for i in range(m):
  a,b=map(int,input().split())
  fri[a-1].append(b-1)
  fri[b-1].append(a-1)
i=0
mx=0
cc=0
while i<n:
  if tab[i]:
    i+=1
    continue
  que=[i]
  cnt=0
  while len(que)!=0:
    run(que.pop())
  mx=max(cnt,mx)
  i+=1
  cc+=1
print(cc-1)

# Buggy version:
import numpy as np
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 i in range(M):
    A, B = map(int, input().split())
    uf.union(A-1, B-1)

print(uf.group_count())

# Fixed version:
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 i in range(M):
  A, B=map(int,input().split())
  A=A-1
  B=B-1
  UF.union(A,B)
print(UF.group_count()-1)

# Buggy version:
import sys
input = sys.stdin.readline

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, a):
        acopy = a
        while a != self.parent[a]:
            a = self.parent[a]
        while acopy != a:
            self.parent[acopy], acopy = a, self.parent[acopy]
        return a

    def union(self, a, b):
        self.parent[self.find(b)] = self.find(a)

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)
o=set()
for i in range(n):
    o.add(UF.fins(i))
print(len(o) - 1)

# Fixed version:
import sys
input = sys.stdin.readline

class UnionFind:
    def __init__(self, n):
        self.parent = list(range(n))

    def find(self, a):
        acopy = a
        while a != self.parent[a]:
            a = self.parent[a]
        while acopy != a:
            self.parent[acopy], acopy = a, self.parent[acopy]
        return a

    def union(self, a, b):
        self.parent[self.find(b)] = self.find(a)

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)
o=set()
for i in range(n):
    o.add(UF.find(i))
print(len(o) - 1)

# Buggy version:
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)

# Fixed version:
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())
group = UnionFind(N)

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

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

# Buggy version:
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 i in range(m):
    a, b = map(int, input().split())
    uf.union(a - 1, b - 1)
print(uf.group_count())

# Fixed version:
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 _i in range(m)]

uf = UnionFind(n)
for i, j in ab:
    uf.union(i-1, j-1)
print(uf.group_count()-1)

# Buggy version:
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())
u = UnionFind(n)

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

a = u.roots()
print(len(a))

# Fixed version:
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)