import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.PrintWriter
import kotlin.math.max

val br = BufferedReader(InputStreamReader(System.`in`))
val out = PrintWriter(System.out)

fun main() {
    br.use { out.use {
//        repeat(readInteger()) { solve() }
        solve()
        out.flush()
    } }
}

fun solve() {
    val (n, m) = readIntegerList()
    val uf = UnionFind(n)
    repeat(m) {
        val (a, b) = readIntegerList().map { it - 1}
        uf.unite(a, b)
    }

    var set = mutableSetOf<Int>()
    for (i in 0 until n) {
        set.add(uf.findRoot(i))
    }
    out.println(set.size - 1)
}

/**　Union find 0 ~ (n - 1)　*/
class UnionFind(n: Int) {
    val parent = IntArray(n) { it }
    val rank = IntArray(n)
    val size = IntArray(n) { 1 }

    /** O(α(n)) */
    fun findRoot(x: Int): Int {
        if (parent[x] == x) return x

        parent[x] = findRoot(parent[x])
        return parent[x]
    }

    /** O(α(n)) */
    fun sameRoot(x: Int, y: Int) = findRoot(x) == findRoot(y)

    /** O(α(n)) */
    fun unite(x: Int, y: Int) {
        val x = findRoot(x)
        val y = findRoot(y)
        if (x == y) return

        if (rank[x] < rank[y]) {
            parent[x] = y
            size[y] += size[x]
        } else {
            parent[y] = x
            if (rank[x] == rank[y]) rank[x]++

            size[x] += size[y]
        }
    }

    /** O(α(n)) */
    fun getSize(x: Int) = size[findRoot(x)]
}


fun readInteger() = Integer.parseInt(br.readLine())
fun readLong() = java.lang.Long.parseLong(br.readLine())
fun readStringList() = br.readLine()!!.split(' ')
fun readIntegerList() = readStringList().map(Integer::parseInt)
fun readLongList() = readStringList().map(java.lang.Long::parseLong)
fun readDoubleList() = readStringList().map(java.lang.Double::parseDouble)
