CodeEff/ECCO huggingface dataset:
# Original solution
#入力:N,M(int:整数)
def input2():
	return list(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
for i in uf.parents:
	if i < 0:
		ans+=1
print((ans-1))


# Optimized solution
#入力:N,M(int:整数)
def input2():
	return list(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))
# Original solution
n, m = list(map(int,input().split()))
lst = [-1 for i in range(n + 1)]
lst[0] = 0
def find (x):
    if (lst[x] < 0):
        return(x)
    else:
        return (find(lst[x]))
def unit(x, y):
    xp = find(x)
    yp = find(y)
    if (xp == yp):
        pass
    else:
        lst[yp] = xp
for i in range(m):
    a, b = list(map(int,input().split()))
    unit(a, b)

ans = -1
for i in range(n + 1):
    if (lst[i] == -1):
        ans = ans + 1

print(ans)


# Optimized solution
n, m = list(map(int,input().split()))
lst = [-1 for i in range(n + 1)]
lst[0] = 0
def find (x):
    if (lst[x] < 0):
        return(x)
    else:
        z = find(lst[x])
        lst[x] = z
        return (z)
def unit(x, y):
    xp = find(x)
    yp = find(y)
    if (xp == yp):
        pass
    else:
        lst[yp] = xp
for i in range(m):
    a, b = list(map(int,input().split()))
    unit(a, b)

ans = -1
for i in range(n + 1):
    if (lst[i] == -1):
        ans = ans + 1

print(ans)
# Original solution
import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.buffer.readline

N, M = list(map(int, input().split()))
nextcity = [[] for _ in range(N)]
sgn = [0 for _ in range(N)]
while M:
	M -= 1
	A, B = list(map(int, input().split()))
	A -= 1
	B -= 1
	nextcity[A].append(B)
	nextcity[B].append(A)

def dfs(cnt, city):
	for item in nextcity[city]:
		if sgn[item] == 0:
			sgn[item] = cnt
			dfs(cnt, item)
	return None

cnt = 0
for k in range(N):
	if sgn[k] == 0:
		cnt += 1
		sgn[k] = cnt
		dfs(cnt, k)
print((cnt -1))


# Optimized solution
import sys
sys.setrecursionlimit(10**9)
input = sys.stdin.buffer.readline

N, M = list(map(int, input().split()))
nextcity = [[] for _ in range(N)]
sgn = [0 for _ in range(N)]
while M:
	M -= 1
	A, B = list(map(int, input().split()))
	A -= 1
	B -= 1
	nextcity[A].append(B)
	nextcity[B].append(A)

def bfs(cnt, lis):
	nextvisit = []
	for j in lis:
		for item in nextcity[j]:
			if sgn[item] == 0:
				nextvisit.append(item)
				sgn[item] = cnt
	if nextvisit:
		bfs(cnt, nextvisit)
		return None
	else:
		return None
cnt = 0
for k in range(N):
	if sgn[k] == 0:
		cnt += 1
		sgn[k] = cnt
		bfs(cnt, [k])
print((cnt -1))
# Original solution
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())
if __name__ == '__main__':
    n, m = list(map(int, input().split()))
    uf = UnionFind(n)
    for i in range(m):
        a, b = list(map(int, input().split()))
        a -= 1
        b -= 1
        uf.union(a, b)

    print((len(list(uf.all_group_members().values()))-1))


# Optimized solution
import sys

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())
if __name__ == '__main__':
    input = sys.stdin.readline
    n, m = list(map(int, input().split()))
    uf = UnionFind(n)
    for i in range(m):
        a, b = list(map(int, input().split()))
        a -= 1
        b -= 1
        uf.union(a, b)

    print((uf.group_count()-1))
# Original solution
# -*- coding: utf-8 -*-

from heapq import heappop, heappush
    

def resolve(T,v):
    while v != T[v]:
        v = T[v]
    return v

def solve():
    N, M = list(map(int, input().split()))
    E = set()
    for _ in range(M):
        a, b = list(map(int, input().split()))
        E.add((a-1,b-1))

    T = list(range(N))
    while E:
        e = E.pop()
        u, v = e
        lu = resolve(T,u)
        lv = resolve(T,v)
        if lv == lu:
            continue
        T[lu] = lv
        continue

    return len(set(resolve(T,v) for v in range(N)))-1

if __name__ == '__main__':
    print((solve()))



# Optimized solution
# -*- coding: utf-8 -*-

def resolve(T,v):
    lv = v
    while lv != T[lv]:
        lv = T[lv]
    T[v] = lv
    return lv

def solve():
    N, M = list(map(int, input().split()))
    T = list(range(N+1))
    for i in range(M):
        u, v = list(map(int, input().split()))
        lu = resolve(T,u)
        lv = resolve(T,v)
        if lv == lu:
            continue
        T[lv] = lu
        #print(f'===round{i}===')
        #print(T)
        #print([resolve(T,v) for v in range(N+1)])
    return len(set(resolve(T,v) for v in range(1,N+1)))-1

if __name__ == '__main__':
    print((solve()))
# Original solution
import sys,io,os
I = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
#I = input

n,m = list(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=list(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)



# Optimized solution
import sys,io,os
I = io.BytesIO(os.read(0, os.fstat(0).st_size)).readline
#I = input

n,m = list(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=list(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)
# Original solution
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=list(map(int,input().split()))
u=UnionFind(n)
for i in range(m):
    a,b=list(map(int,input().split()))
    a-=1
    b-=1
    u.unite(a,b)
ans=0
for i in range(n):
    ans=u.roots()
print((len(ans)-1))


# Optimized solution
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=list(map(int,input().split()))
u=UnionFind(n)
for i in range(m):
    a,b=list(map(int,input().split()))
    a-=1
    b-=1
    u.unite(a,b)
ans=u.roots()
print((len(ans)-1))
# Original solution
import sys
from collections import deque


N, M = list(map(int, input().split()))
edge = [[] for _ in range(N)]
for s in sys.stdin.readlines():
    a, b = [int(x) - 1 for x in 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))


# Optimized solution
import sys
from collections import deque


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

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

print((cnt - 1))
# Original solution
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=list(map(int,input().split()))
A=[0]*M
B=[0]*M
for i in range(M):
    A[i],B[i]=list(map(int,input().split()))

uf=UnionFind(N+1)
for i in range(M):
    uf.union(A[i],B[i])
    #print(uf.find(1))
#print()
a=[0]*N
#print(uf)
print((len(list(uf.all_group_members().values()))-2))


# Optimized solution
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=list(map(int,input().split()))
A=[0]*M
B=[0]*M
for i in range(M):
    A[i],B[i]=list(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))
# Original solution
import collections
n, m = list(map(int, input().split()))
linked = collections.defaultdict(set)
for i in range(1, n + 1):
  linked[i] = [i]
 
for i in range(m):
  a, b = list(map(int, input().split()))
  if linked[a] == linked[b]: continue
  linked[a].extend(linked[b])
  for t in linked[b]:
    linked[t] = linked[a]
 
n_connected = 0
n_group = 0
for s in list(linked.values()):
  if s:
    n_group += 1
    n_connected += len(s)
    s.clear()
print(((n - n_connected) + (n_group - 1)))

# Optimized solution
import collections
n, m = list(map(int, input().split()))
linked = collections.defaultdict(list)
i2group = dict()

gid = n + 1
def get_root(i):
  if i not in linked:
    return i
  j = get_root(linked[i])
  linked[i] = j
  return j

def get_groups():
  return [get_root(i) for i in range(1, n + 1)]

for i in range(m):
  a, b = list(map(int, input().split()))
  ra, rb = get_root(a), get_root(b)
  if ra == rb: continue
  linked[ra] = gid
  linked[rb] = gid
  gid += 1

g = get_groups()
print((len(set(g)) - 1))
exit()

n_connected = 0
n_group = 0
for s in list(linked.values()):
  s = s[0]
  if s:
    n_group += 1
    n_connected += len(s)
    s.clear()
print(((n - n_connected) + (n_group - 1)))
# Original solution
def main():
    N, M = list(map(int, input().split()))
    connect_list = [0] * N

    max_road_num = 0
    for i in range(M):
        A, B = list(map(int, input().split()))
        A = A - 1
        B = B - 1
        
        A_road_num = connect_list[A]
        B_road_num = connect_list[B]

        if A_road_num == 0 and B_road_num == 0:
            max_road_num = max_road_num + 1
            connect_list[A] = max_road_num
            connect_list[B] = max_road_num
        elif A_road_num != 0 and B_road_num == 0:
            connect_list[B] = connect_list[A]
        elif A_road_num == 0 and B_road_num != 0:
            connect_list[A] = connect_list[B]
        else:
            connect_list = [A_road_num if B_road_num == road_num else road_num for road_num in connect_list]

    answer = 0
    set_connect_list = set(connect_list)
    set_connect_list.discard(0)
    if len(set_connect_list) != 1:
        answer = len(set_connect_list) - 1

    print((answer + connect_list.count(0)))

if __name__ == "__main__":
    main()

# Optimized solution
def root(list_parent, x):
    if(list_parent[x] == x):
        return x
    else:
        list_parent[x] = root(list_parent, list_parent[x])
        return list_parent[x]

def unite(list_parent, x, y):
    x = root(list_parent, x)
    y = root(list_parent, y)

    if(x == y):
        return list_parent

    if x > y:
        list_parent[y] = x
    else:
        list_parent[x] = y

    return list_parent

def main():
    N, M = list(map(int, input().split()))

    list_parent = []
    for i in range(N):
        list_parent.append(i)

    for i in range(M):
        A, B = list(map(int, input().split()))
        list_parent = unite(list_parent, A-1, B-1)

    set_root = set()
    for i in list_parent:
        root_i = root(list_parent, i)
        if root_i not in set_root:
            set_root.add(root_i)

    print((len(set_root) - 1))

if __name__ == "__main__":
    main()
# Original solution
# import sys
# import math #sqrt,gcd,pi
# import decimal
import queue # queue
import bisect
# import heapq # priolity-queue
# from time import time
# from itertools import product,permutations,\
    # combinations,combinations_with_replacement
# 重複あり順列、順列、組み合わせ、重複あり組み合わせ
# import collections # deque
# from operator import itemgetter,mul
# from fractions import Fraction
# from functools import reduce

mod = int(1e9+7)
# mod = 998244353
INF = 1<<50

def readInt():
    return list(map(int,input().split()))

def main():
    n,m = readInt()
    road = [[] for i in range(n)]
    for i in range(m):
        a,b = readInt()
        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):
        q = queue.Queue()
        q.put(city.pop())
        visit = set()
        while not q.empty():
            x = q.get()
            visit.add(x)
            for i in road[x]:
                if i not in visit:
                    q.put(i)
                    city.discard(i)
        if len(city):
            ans += 1
    print(ans)
    return

if __name__=='__main__':
    main()


# Optimized solution
# queueのライブラリをインポート
import queue

n,m = list(map(int,input().split()))
# 各都市から繋がっている都市を記録
road = [[] for i in range(n)]
for i in range(m):
  a,b = list(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)
# Original solution
v, e = list(map(int,input().split()))
par = [i for i in range(v+1)]

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

for i in range(e):
  a, b = list(map(int, input().split()))
  unite(a, b)
  
cnt = 0
for i in range(v+1):
  if i == par[i]:
    cnt += 1

print((cnt-2))

# Optimized solution
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())
  
v, e = list(map(int, input().split()))
uf = UnionFind(v)
for i in range(e):
  a, b = list(map(int, input().split()))
  uf.union(a-1, b-1)
  
print((uf.group_count()-1))
# Original solution
from collections import deque
n, m = list(map(int,input().split()))

edge = [[] for _ in range(n)]
for i in range(m):
    a, b = list(map(int,input().split()))
    edge[a-1].append(b-1)
    edge[b-1].append(a-1)
    
visited = [0]*n
visited[0] = 1
q = deque([0])

ans = 0

while sum(visited) < n:
    while q:
        now = q.popleft()
        for to in edge[now]:
            if visited[to] == 0:
                q.append(to)
                visited[to] = 1
    #print(visited)
    if sum(visited) < n:
        ans += 1
        for i,v in enumerate(visited):
            if v == 0:
                q.append(i)
                visited[i] = 1
                break

print(ans)

# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n
 
    def find(self, x):#要素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が属するグループと要素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):#要素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())
 
    def all_group_members(self):#{ルート要素: [そのグループに含まれる要素のリスト], ...}の辞書を返す
        return {r: self.members(r) for r in self.roots()}
 
    def __str__(self):#print()での表示用 ルート要素: [そのグループに含まれる要素のリスト]を文字列で返す
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

n, m = list(map(int,input().split()))
uf = UnionFind(n)

edge = []
for i in range(m):
    a,b = list(map(int,input().split()))
    edge.append((a-1,b-1))
    
for a,b in edge:
    uf.union(a, b)
    
print((uf.group_count()-1))