package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
)

type splitter int

const (
	bySpace splitter = iota
	byLine
)

func newScanner(sp splitter) *sc {
	scanner := bufio.NewScanner(os.Stdin)
	if sp == bySpace {
		scanner.Split(bufio.ScanWords)
	}
	return &sc{
		s: scanner,
	}
}

type sc struct {
	s *bufio.Scanner
}

func (s *sc) nextInt() int {
	if s.s.Scan() {
		val := s.s.Text()
		v, err := strconv.Atoi(val)
		if err != nil {
			panic(err)
		}
		return v
	}
	panic("no more stdin")
}

type node struct {
	id int
	isVisited bool
	root int
	connected []*node
}

func main() {
	sc := newScanner(bySpace)
	n := sc.nextInt()
	m := sc.nextInt()
	nodes := make([]*node, n)
	for i := 0; i < n; i++ {
		nodes[i] = &node{
			id: i,
			root: i,
		}
	}
	for i := 0; i < m; i++ {
		a := sc.nextInt() - 1
		b := sc.nextInt() - 1
		nodes[a].connected = append(nodes[a].connected, nodes[b])
		nodes[b].connected = append(nodes[b].connected, nodes[a])
	}
	for _, n := range nodes {
		solve(n, n.id)
	}
	sum := 0
	for _, n := range nodes {
		if n.id == n.root {
			sum ++
		}
	}
	fmt.Println(sum - 1)
}

func solve(n *node, i int) {
	if n.isVisited {
		return 
	}
	n.isVisited = true
	n.root = i
	for _, m := range n.connected {
		solve(m, i)
	}
}