#include <bits/stdc++.h>
using namespace std;
template <typename T>
T gcd(T a, T b) {
  if (!b) return a;
  return gcd(b, a % b);
}
template <typename T>
T lcm(T a, T b) {
  return a * b / gcd(a, b);
}
int a, b, i, j, n, ans = 0;
vector<int> graph[110];
vector<pair<int, int> > vp;
struct UnionFind {
  int N, *id, *sz;
  UnionFind(int N) {
    this->N = N;
    id = new int[N];
    sz = new int[N];
    for (int i = 0; i < N; i++) {
      id[i] = i;
      sz[i] = 1;
    }
  }
  ~UnionFind() {
    delete[] id;
    delete[] sz;
  }
  int root(int i) {
    while (i != id[i]) {
      id[i] = id[id[i]];
      i = id[i];
    }
    return i;
  }
  bool find(int p, int q) { return root(p) == root(q); }
  void unite(int p, int q) {
    int i = root(p);
    int j = root(q);
    if (i == j) return;
    if (sz[i] < sz[j]) {
      id[i] = j;
      sz[j] += sz[i];
    } else {
      id[j] = i;
      sz[i] += sz[j];
    }
  }
};
struct edge {
  int a, b;
  edge() {}
  edge(int a, int b) : a(a), b(b) {}
};
int main(void) {
  scanf("%d", &n);
  for (i = 0; i < (n); i++) {
    scanf("%d%d", &a, &b);
    vp.push_back(make_pair(a, b));
  }
  UnionFind uf(n);
  vector<edge> ve;
  for (i = 0; i < (n); i++) {
    for (j = 0; j < (n); j++) {
      if (vp[i].first == vp[j].first || vp[i].second == vp[j].second) {
        uf.unite(i, j);
      } else {
        ve.push_back(edge(i, j));
      }
    }
  }
  for (i = 0; i < (ve.size()); i++) {
    if (!uf.find(ve[i].a, ve[i].b)) {
      ans += 1;
      uf.unite(ve[i].a, ve[i].b);
    }
  }
  printf("%d\n", ans);
  return 0;
}
