#include <bits/stdc++.h>

using namespace std;

template<typename S, typename T>
ostream& operator<<(ostream& out, const pair<S, T> p) {
    out << "(" << p.first << "," << p.second << ")";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const vector<T>& v) {
    for (auto a: v)
        out << a << " ";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const set<T>& S) {
    for (auto a: S)
        cout << a << " ";
    return out;
}

template<typename T>
ostream& operator<<(ostream& out, const multiset<T>& S) {
    for (auto a: S)
        cout << a << " ";
    return out;
}

template<typename S, typename T>
ostream& operator<<(ostream& out, const map<S, T>& M) {
    for (auto m: M)
        cout << "(" << m.first << "->" << m.second << ") ";
    return out;
}

template<typename S, typename T>
pair<S, T> operator+(pair<S, T> a, pair<S, T> b) {
    return make_pair(a.first + b.first, a.second + b.second);
}

template<typename S, typename T>
pair<S, T> operator-(pair<S, T> a, pair<S, T> b) {
   return make_pair(a.first - b.first, a.second - b.second);
}

struct DisjointSetUnion {
    int n;
    vector<int> parent;
    vector<int> rank;
    vector<int> numv;
    int group_size;
    map<int, int> M;

    void init(int n) {
        this->n = n;
        parent.resize(n);
        for (int i = 0; i < n; i++)
            parent[i] = i;
        rank.assign(n, 0);
        numv.assign(n, 1);
        group_size = n;
    }

    int query_group_leader(int a) {
        while (parent[a] != a)
            a = parent[a];
        return a;
    }

    void union_group(int a, int b) {
        int s = query_group_leader(a);
        int t = query_group_leader(b);

        if (s != t) {
            if (rank[s] < rank[t]) {
                parent[s] = t;
                numv[t] += numv[s];
            } else if (rank[s] == rank[t]) {
                parent[s] = t;
                rank[t]++;
                numv[t] += numv[s];
            } else if (rank[s] > rank[t]) {
                parent[t] = s;
                numv[s] += numv[t];
            }
            group_size--;
        }
    }

    int query_group_size() {
        return group_size;
    }

    int query_group_member_size(int a) {
        return numv[query_group_leader(a)];
    }

    vector<int> query_all_group_leader() {
        vector<int> A;
        for (int i = 0; i < n; i++)
            if (parent[i] == i)
                A.push_back(i);
        return A;
    }

    void pushdown() {
        vector<int> A = query_all_group_leader();
        M.clear();
        for (int i = 0; i < A.size(); i++)
            M[A[i]] = i;
    }

    int query_group_id_after_pushdown(int a) {
        return M[query_group_leader(a)];
    }

    vector<vector<int>> query_group_after_pushdown() {
        // cout << "M: " << M << endl;
        vector<vector<int>> ans(M.size());
        for (int i = 0; i < n; i++) {
            ans[M[query_group_leader(i)]].push_back(i);
        }
        return ans;
    }

    void dump() {
        cout << "n: " << n << endl;
        cout << "parent: " << parent << endl;
        cout << "rank: " << rank << endl;
        cout << "group_size: " << query_group_size() << endl;
        vector<int> leader = query_all_group_leader();
        pushdown();
        cout << "M: " << M << endl;
        vector<vector<int>> group = query_group_after_pushdown();
        for (int i = 0; i < group.size(); i++) {
            cout << "group[" << i << "]: " << group[i] << ", ";
            int s = leader[i];
            cout << "numv[" << s << "]: " << numv[s] << endl;
        }
        vector<int> group_id;
        for (int i = 0; i < n; i++)
            group_id.push_back(query_group_id_after_pushdown(i));
        cout << "group_leader: " << leader << endl;
        cout << "group_id: " << group_id << endl;
        cout << "numv: " << numv << endl;
        cout << "end of dump" << endl;
    }
};

DisjointSetUnion dsu;

int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int n, m;
    cin >> n >> m;
    dsu.init(n);
    while (m--) {
        int a, b;
        cin >> a >> b;
        a--, b--;
        dsu.union_group(a, b);
    }
    // dsu.dump();
    int ans = dsu.group_size - 1;
    cout << ans << endl;
    return 0;
}


Measured memory_bytes: 4420