#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
using namespace std;

#define sim template <class c
#define ris return *this
#define dor > debug& operator<<
#define eni(x)                                                                 \
    sim > typename enable_if<sizeof dud<c>(0) x 1, debug&>::type operator<<(   \
              c i) {
sim > struct rge { c b, e; };
sim > rge<c> range(c i, c j) { return rge<c>{i, j}; }
sim > auto dud(c* x) -> decltype(cerr << *x, 0);
sim > char dud(...);
struct debug {
#ifndef LOCAL
    ~debug() { cerr << endl; }
    eni(!=) cerr << boolalpha << i;
    ris;
} eni(==) ris << range(begin(i), end(i));
}
sim, class b dor(pair<b, c> d) {
    ris << "(" << d.first << ", " << d.second << ")";
}
sim dor(rge<c> d) {
    *this << "[";
    for (auto it = d.b; it != d.e; ++it)
        *this << ", " + 2 * (it == d.b) << *it;
    ris << "]";
}
#else
    sim dor(const c&) { ris; }
#endif
}
;
#define imie(...) " [" << #__VA_ARGS__ ": " << (__VA_ARGS__) << "] "

template <typename T>
using indexed_set =
    tree<T, null_type, less<T>, rb_tree_tag, tree_order_statistics_node_update>;

#define int long long
#define ld long double
#define pb push_back
#define mp make_pair
#define in insert
#define fi first
#define se second
#define lb lower_bound
#define ub upper_bound
#define sz(v) (int)(v.size())
#define all(x) x.begin(), x.end()
#define allr(x) x.rbegin(), x.rend()
#define mem(a, s) memset(a, s, sizeof(a))
#define rep(i, a, b) for (int i = a; i <= b; ++i)
#define repr(i, a, b) for (int i = a; i >= b; --i)
#define MAX 8000000000000000064LL
#define MIN -8000000000000000064LL

typedef vector<int> vi;
typedef stack<int> sti;
typedef queue<int> qi;
typedef pair<int, int> pii;
typedef set<int> si;
typedef map<int, int> mii;
typedef vector<pii> vpi;

const int mod = 1e9 + 7;
int fact[1000007] = {0};

int add(int a, int b) {
    return (a % mod + b % mod + ((MAX) / mod) * mod) % mod;
}

int sub(int a, int b) {
    return (a % mod - b % mod + ((MAX) / mod) * mod) % mod;
}

int mul(int a, int b) {
    return ((a % mod) * (b % mod) + ((MAX) / mod) * mod) % mod;
}

int lcm(int a, int b) { return (a * b) / __gcd(a, b); }

int expo(int x, int y) {
    int res = 1;
    x = x % mod;
    while (y > 0) {
        if (y & 1)
            res = (1LL * res * x) % mod;
        y = y >> 1;
        x = (1ll * x * x) % mod;
    }
    return res;
}

void facto() {
    fact[0] = 1;
    fact[1] = 1;
    rep(i, 2, 1000006) fact[i] = (fact[i - 1] * i) % mod;
}

int ncr(int n, int r) {
    int res = 1;
    res = fact[n];
    res = (res * (expo(fact[r], mod - 2))) % mod;
    res = (res * (expo(fact[n - r], mod - 2))) % mod;
    return res;
}

int npr(int n, int r) {
    facto();
    int res = 1;
    res = fact[n];
    res = (res * (expo(fact[n - r], mod - 2))) % mod;
    return res;
}

int _x[] = {0, 0, -1, 1, -1, 1, -1, 1};
int _y[] = {-1, 1, 0, 0, 1, -1, -1, 1};

void pre() {}

const int mxn = 1e5 + 10;
int parent[mxn];

int find(int x) {
    if (parent[x] == x)
        return x;
    return parent[x] = find(parent[x]);
}

void uni(int x, int y) {
    x = find(x);
    y = find(y);
    if (x == y)
        return;
    parent[x] = y;
}

void solve() {
    int n, m;
    cin >> n >> m;
    rep(i, 1, n) parent[i] = i;
    rep(i, 0, m - 1) {
        int x, y;
        cin >> x >> y;
        uni(x, y);
    }
    int ans = 0;
    rep(i, 1, n) if (parent[i] == i) ans++;
    cout << ans - 1;
}

int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);
    cout.tie(NULL);
    pre();
    int t = 1;
    // cin >> t;
    rep(i, 1, t) {
        // cout << "Case #" << i << " : ";
        solve();
    }
    return 0;
}


Measured memory_bytes: 4400