#include<bits/stdc++.h>
 
using namespace std;
 
#define lli long long
typedef long double ldb;
#define deci(n) fixed << setprecision(n)
 
lli M = 1e9+7;
lli MOD = 1e9+7;
 
#define MAXN 2000006 
 
// Template function //
lli powermod(lli x, lli y)  
{  
    lli res = 1;  
    x = x % MOD;
    if (x == 0) return 0;  
    while (y > 0)  
    {  
        if (y & 1)  
            res = (res*x) % MOD;  
        y = y>>1;   
        x = (x*x) % MOD;  
    }  
    return res;  
}
lli modinv(lli x)
{
    return powermod(x,MOD-2);
}
// Template functions end //
 
// Custom funtions //
lli findset(lli p[], lli x){
	if(x!=p[x]){
		p[x]=findset(p,p[x]);
	}
	return p[x];
}
 
void unionset(lli p[],lli r[],lli a,lli b){
	lli x=findset(p,a);
	lli y=findset(p,b);
	if(x!=y){
		if(r[x]<r[y]) swap(x,y);
		p[y]=x;
		if(r[x]==r[y]){
			r[x]++;
		}
	}
}

void dfs(vector<lli> v[], lli s[], lli x)
{
    lli S=1;
    for(lli i=0;i<v[x].size();i++)
    {
        dfs(v,s,v[x][i]);
        S+=s[v[x][i]];
    }
    s[x]=S;
}

lli f[100005];



void preprocess()
{
    f[0]=1;
    for(lli i=1;i<100005;i++)
    {
        f[i] = (f[i-1]*i)%MOD;
        if(i%2==0)
        f[i]/=2;
    }
}

lli tree[MAXN];


void buildTree(vector<lli> a, lli n)
{
    for(lli i=0;i<n;i++)
        tree[n+i] = a[i];
    for(lli i=n-1;i>0;i--)
    {
        tree[i] = tree[i<<1] + tree[i<<1|1];
    }
}

void updateTree(lli n, lli p, lli value)
{
    tree[p+n] = value;
    p+=n;
    for(lli i=p; i>1;i >>= 1)
        tree[i>>1] = tree[i] + tree[i^1];
}

lli queryTree(lli n, lli l, lli r)
{
    lli res = 0;
    for(l+=n,r+=n;l<r;l>>=1, r>>=1)
    {
        if(l&1)
            res += tree[l++];
        if(r&1)
            res += tree[--r];
    }
    return res;
}

bool sortbysec(const pair<pair<lli,lli>,lli> &a, 
              const pair<pair<lli,lli>,lli> &b) 
{ 
    if(a.first.second == b.first.second)
    return a.first.first>b.first.first;
    return (a.first.second < b.first.second); 
} 

void dfs(int x, int vi[], vector<int> v[])
{
    vi[x] = 1;
    for(int i=0;i<v[x].size();i++) 
    {
        if(vi[v[x][i]]==0)
        {
            dfs(v[x][i],vi,v);
        }
    }
}

#define MAXN   1000005 
  
// stores smallest prime factor for every number 
int spf[MAXN]; 
  

void sieve() 
{ 
    spf[1] = 1; 
    for (int i=2; i<MAXN; i++) 
  
        spf[i] = i; 
  
    for (int i=4; i<MAXN; i+=2) 
        spf[i] = 2; 
  
    for (int i=3; i*i<MAXN; i++) 
    { 
        if (spf[i] == i) 
        { 
            for (int j=i*i; j<MAXN; j+=i) 
   
                if (spf[j]==j) 
                    spf[j] = i; 
        } 
    } 
} 

set<int> getFactorization(int x) 
{ 
    set<int> ret; 
    while (x != 1) 
    { 
        ret.insert(spf[x]); 
        x = x / spf[x]; 
    } 
    return ret; 
}

bool pos(lli c[], lli d, lli m, lli n)
{
    lli k = c[0];
    for(int i=1;i<n;i++)
    {
        k = max(k+m,c[i]);
        if(c[i]+d<k)
        {
            return false;
        }
    }
    return true;
}

void solve()
{ 
    int n,m;
    cin>>n>>m;
    vector<int> e[n];
    int x,y;
    for(int i=0;i<m;i++)
    {
        cin>>x>>y;
        x--; y--;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    int vi[n]={0};
    int ans = -1;
    for(int i=0;i<n;i++)
    {
        if(vi[i]==0)
        {
            dfs(i, vi, e);
            ans++;
        }
    }
    cout<<ans<<endl;

}
// Custom functions end //
 
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    lli q=1;
    // cin>>q;
    for(lli tc=1;tc<=q;tc++)
    {
        solve();
    }
    return 0;
}


Measured memory_bytes: 10396