#include<cstdio>
#include<vector>

#define rep(i,n) for(int i=0;i<(n);i++)

using namespace std;

class union_find{
	int n;
	vector<int> a;
public:
	union_find(int N):a(N,-1),n(N){}
	int find(int x){
		if(a[x]<0) return x;
		return a[x]=find(a[x]);
	}
	void unite(int x,int y){
		x=find(x),y=find(y);
		if(x!=y){ a[x]+=a[y]; a[y]=x; n--; }
	}
	int size(){ return n; }
};

struct slime{ int x,y,id; };

int main(){
	int n,w,h; scanf("%d%d%d",&n,&w,&h);
	static slime s[40000];
	rep(i,n) scanf("%d%d",&s[i].x,&s[i].y), s[i].x--, s[i].y--, s[i].id=i;

	static vector<slime> X[100000],Y[100000];
	rep(i,n){
		X[s[i].x].push_back(s[i]);
		Y[s[i].y].push_back(s[i]);
	}

	union_find U(n);
	rep(j,w) rep(k,(int)X[j].size()-1) U.unite(X[j][k].id,X[j][k+1].id);
	rep(i,h) rep(k,(int)Y[i].size()-1) U.unite(Y[i][k].id,Y[i][k+1].id);

	bool corner=false;
	rep(i,n) if(s[i].x==0 || s[i].x==w-1 || s[i].y==0 || s[i].y==h-1) corner=true;

	int ans=0;
	ans+=n-U.size(); // 1 回の移動で合体できるスライムたちをまとめる
	if(U.size()>=2){
		ans+=2*U.size()-1; // どこかの外壁に並べる
		if(corner) ans--;
	}
	printf("%d\n",ans);

	return 0;
}