#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cassert>
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

#define REP(i, n) for (int i = 0; i < (int)(n); ++i)
#define FOR(i, a, b) for (int i = (a); i < (int)(b); ++i)
#define FOREQ(i, a, b) for (int i = (a); i <= (int)(b); ++i)
#define ALL(c) (c).begin(), (c).end()
#define FOREACH(i, c) for (auto i = (c).begin(); i != (c).end(); ++i)

struct Point {
  int y, x, index;
  Point(): y(0), x(0), index(0) {}
  Point(int y, int x, int index): y(y), x(x), index(index) {}
};

struct LessY {
  bool operator()(const Point& lhs, const Point& rhs) const { return lhs.y < rhs.y; }
};

struct LessX {
  bool operator()(const Point& lhs, const Point& rhs) const { return lhs.x < rhs.x; }
};

vector<Point> slimes, ysorted, xsorted;
vector<bool> visit;

int dfs(int i) {
  if (visit[i]) { return 0; }
  visit[i] = true;

  int ret = 1;
  auto range1 = equal_range(ALL(ysorted), Point(slimes[i].y, 0, 0), LessY());
  for (auto it = range1.first; it != range1.second; ++it) { ret += dfs(it->index); }
  auto range2 = equal_range(ALL(xsorted), Point(0, slimes[i].x, 0), LessX());
  for (auto it = range2.first; it != range2.second; ++it) { ret += dfs(it->index); }
  return ret;
}

int main() {
  int N, W, H;
  while (scanf("%d%d%d", &N, &W, &H) != EOF) {
    slimes.resize(N);
    ysorted.resize(N);
    xsorted.resize(N);
    visit.assign(N, false);

    int wallside = 0;
    REP(i, N) {
      int y, x; scanf("%d%d", &x, &y);
      slimes[i] = Point(y, x, i);
      ysorted[i] = xsorted[i] = slimes[i];
      if (y == 1 || y == H || x == 1 || x == W) { wallside = 1; }
    }
    sort(ALL(ysorted), LessY());
    sort(ALL(xsorted), LessX());

    int ans = 0, n_cc = 0;
    REP(i, N) {
      if (!visit[i]) {
        ans += dfs(i)-1;
        n_cc += 1;
      }
    }
    if (n_cc > 1) { ans += 2*n_cc - wallside - 1; }

    printf("%d\n", ans);
  }
}