Problem:
Problem D: King Slime

There is a grid of size W × H surrounded by walls. Some cells in the grid are occupied by slimes. The slimes want to unite with each other and become a "King Slime".

In each move, a slime can move to east, west, south and north direction until it enters a cell occupied by another slime or hit the surrounding wall. If two slimes come together, they unite and become a new slime.

Your task is write a program which calculates the minimum number of moves that all the slimes unite and become a King Slime. Suppose slimes move one by one and they never move simultaneously.

Input

The first line contains three integers N (2 ≤ N ≤ 40,000), W and H (1 ≤ W, H ≤ 100,000), which denote the
number of slimes, the width and the height of the grid respectively.

The following N lines describe the initial coordinates of the slimes. The i-th line contains two integers xi (1 ≤ xi ≤ W) and yi (1 ≤ yi ≤ H), which indicate the coordinates of the i-th slime . All the coordinates are 1-based.

You may assume that each cell is occupied by at most one slime initially.

Output

Output the minimum number of moves that all the slimes unite and become a King Slime.

Sample Input 1

4 3 3
1 1
1 3
3 1
3 3

Output for the Sample Input 1

3

Sample Input 2

2 3 3
2 2
3 3

Output for the Sample Input 2

2

Sample Input 3

2 4 4
2 2
3 3

Output for the Sample Input 3

3

Sample Input 4

2 4 4
2 2
2 3

Output for the Sample Input 4

1

Solution in C++11 (judge verdict: Accepted)

#include <bits/stdc++.h>
using namespace std;

#define rep(i,x,y) for(int i=(x);i<(y);++i)
#define debug(x) #x << "=" << (x)

#ifdef DEBUG
#define _GLIBCXX_DEBUG
#define dump(x) std::cerr << debug(x) << " (L:" << __LINE__ << ")" << std::endl
#else
#define dump(x)
#endif

typedef long long int ll;
typedef pair<int,int> pii;
//template<typename T> using vec=std::vector<T>;

const int inf=1<<30;
const long long int infll=1LL<<58;
const double eps=1e-9;
const int dx[]={1,0,-1,0},dy[]={0,1,0,-1};

template <typename T> ostream &operator<<(ostream &os, const vector<T> &vec){
    os << "[";
    for (const auto &v : vec) {
    	os << v << ",";
    }
    os << "]";
    return os;
}

class union_find{
	private:
	vector<int> parent,rank;
	int size;
	public:
    int cnt;

    union_find()=default;
    union_find(int n){ init(n); }

    void init(int n){
		size=n;
		cnt=n;
		parent.resize(size);
		rank.resize(size);
		for(int i=0; i<size; ++i){
			parent[i]=i;
			rank[i]=0;
		}
	}

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

	void unite(int x,int y){
		x=find(x);
		y=find(y);
		if(x==y) return;
		if(rank[x]<rank[y]){
			parent[x]=y;
		} else {
			parent[y]=x;
			if(rank[x]==rank[y]) ++rank[x];
		}
		--cnt;
	}

	bool is_same(int x,int y){
		return find(x)==find(y);
	}
};

void solve(){
    int n,w,h;
    cin >> n >> w >> h;

    union_find uf(n);
    vector<array<bool,4>> flags;
    map<int,vector<pii>> mp1,mp2;
    rep(i,0,n){
        int x,y;
        cin >> x >> y;
        --x;
        --y;
        mp1[x].emplace_back(make_pair(y,i));
        mp2[y].emplace_back(make_pair(x,i));
        flags.emplace_back(array<bool,4>{x==0,y==0,x==w-1,y==h-1});
    }

    auto update=[](array<bool,4> &a,array<bool,4> &b){
        rep(i,0,4) a[i]|=b[i];
    };
    int cnt_move=0;
    for(auto it=mp1.begin(); it!=mp1.end(); ++it) for(const auto &p:it->second){
        if(uf.is_same(it->second.front().second,p.second)) continue;
        const int x=uf.find(it->second.front().second),y=uf.find(p.second);
        uf.unite(x,y);
        update(flags[uf.find(p.second)],flags[x]);
        update(flags[uf.find(p.second)],flags[y]);
        ++cnt_move;
    }
    for(auto it=mp2.begin(); it!=mp2.end(); ++it) for(const auto &p:it->second){
        if(uf.is_same(it->second.front().second,p.second)) continue;
        const int x=uf.find(it->second.front().second),y=uf.find(p.second);
        uf.unite(x,y);
        update(flags[uf.find(p.second)],flags[x]);
        update(flags[uf.find(p.second)],flags[y]);
        ++cnt_move;
    }

    if(uf.cnt==1){
        cout << cnt_move << endl;
        return;
    }

    int ans=inf;
    set<int> reps;
    rep(i,0,n) reps.insert(uf.find(i));
    rep(i,0,4){
        int cnt=0;
        for(int idx:reps) if(!flags[idx][i]) ++cnt;
        ans=min(ans,cnt_move+cnt);
    }
    ans+=uf.cnt-1;
    cout << ans << endl;
}

int main(){
    std::ios::sync_with_stdio(false);
    std::cin.tie(0);
    cout << fixed << setprecision(8);
    solve();
    return 0;
}