#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 int           long long
#define S             second
#define F             first
#define pb            push_back
#define all(c)        (c).begin(),(c).end()
#define rall(c)       (c).rbegin(),(c).rend() 
#define lb            lower_bound
#define ub            upper_bound
#define si(c)         (int)((c).size())
#define lcm(a, b)      (a * (b / __gcd(a,b)))  
#define inf           (int)(4e18)
#define endl          '\n'
#define mp            make_pair
#define time(s)       (double(clock()-s)/double(CLOCKS_PER_SEC))
#define debug(args...)    _F(#args, args)
#define vi            std::vector<int>
#define pii           pair<int, int> 
#define vpi           vector<pii>
#define ordered_set   tree<int, null_type,less<int>, rb_tree_tag,tree_order_statistics_node_update> 

clock_t start;
mt19937_64 rng(chrono::system_clock::now().time_since_epoch().count());

template<typename T> void _F(const char *name, T arg1){ cerr << name << " = " << arg1 << endl;}
template<typename T, typename... Args> void _F(const char *names, T arg1, Args... args)
{ const char *name = strchr(names, ',');cerr.write(names, name-names) << " = " << arg1 << endl;_F(name+2, args...);}

template< typename T1, typename T2 > istream& operator>>(istream& in, pair<T1, T2> &q){ in >> q.F >> q.S; return in;}
template< typename T1, typename T2 > ostream& operator<<(ostream& out, pair<T1, T2> &q){ out << q.F << " " << q.S; return out;}
template< typename T1, typename T2 > pair<T1, T2> operator+(pair<T1, T2> p1, pair<T1, T2> p2){ return {p1.F+p2.F, p1.S+p2.S};}
template< typename T1, typename T2 > pair<T1, T2> operator-(pair<T1, T2> p1, pair<T1, T2> p2){ return {p1.F-p2.F, p1.S-p2.S};}
template< typename T1, typename T2 > bool operator<(pair<T1, T2> p1, pair<T1, T2> p2){ return p1 < p2 ;}

template<typename T> void Unique(vector<T> &v) {
    sort(all(v)), v.resize(distance(v.begin(), unique(all(v))));
}

struct Dsu {
	int comps, n;
	std::vector<int> par, siz;

	Dsu(int _n) {
		n = _n;
		par.resize(n+1), siz.resize(n+1);
		for(int i = 0; i <= n; i++)
			par[i] = i, siz[i] = 1;
		comps = n;
	}
	int root(int x) {
		return (par[x] == x ? x : par[x] = root(par[x]));
	}
	void merge(int x, int y) {
		x = root(x), y = root(y);
		if(x == y) return;
		if(siz[y] > siz[x]) swap(x, y);
		siz[x] += y;
		par[y] = x;
		comps--;
	}
	std::vector<vi> get_component() {
		std::vector<std::vector<int> > ans;
		std::vector<int> vis(n+1);
		for(int i = 0; i < n; i++) {
			int r = root(i);
			if(vis[r] == -1) {
				vis[r] = si(ans);
				ans.pb(std::vector<int> ());
			}
			ans[vis[r]].pb(i);
		}
		return ans;
	} 
};

void solve() {
	int n, m;
	cin >> n >> m;

	Dsu ds(n);
	for(int i =0; i < m; i++) {
		int a, b;
		cin >> a >> b;
		ds.merge(a, b);
	}
	cout << ds.comps-1 << endl;
}

signed main(){

    ios_base::sync_with_stdio(0); 
    cin.tie(0); cout.tie(0);   
    start = clock(); 

  

    int test = 1;
    // cin >> test;
    
    cout << fixed << setprecision(2);
     
    for(int i = 1; i <= test; ++i){
    	solve();
    }

    cerr << time(start);
    return 0;
}


Measured memory_bytes: 4824