#include <bits/stdc++.h>
using namespace std;
long long int MIN = 100000000000000;
long long int prime[1 << 20];
int numofprimes;
long long int gcd(long long int a, long long int b) {
  return (b == 0 ? a : gcd(b, a % b));
}
long long int lcm(long long int a, long long int b) {
  return (a * (b / gcd(a, b)));
}
long long int POWER(int x, int y, int m) {
  if (y == 0) return 1;
  if (y % 2 == 0) {
    long long int temp = POWER(x, y / 2, m);
    temp *= temp;
    temp %= m;
    return temp;
  }
  long long int temp = POWER(x, y - 1, m);
  temp *= x;
  temp %= m;
  return temp;
}
void runEratosthenesSieve(int upperBound) {
  int upperBoundSquareRoot = (int)sqrt((double)upperBound);
  bool *isComposite = new bool[upperBound + 1];
  memset(isComposite, 0, sizeof(bool) * (upperBound + 1));
  for (int m = 2; m <= upperBoundSquareRoot; m++) {
    if (!isComposite[m]) {
      prime[numofprimes++] = m;
      for (int k = m * m; k <= upperBound; k += m) isComposite[k] = true;
    }
  }
  for (int m = upperBoundSquareRoot + 1; m <= upperBound; m++)
    if (!isComposite[m]) prime[numofprimes++] = m;
  delete[] isComposite;
}
int n, ans;
int x[111], y[111];
bool vis[111];
void dfs(int node) {
  vis[node] = true;
  for (int i = 1; i <= n; i++) {
    if (!vis[i] && (x[i] == x[node] || y[i] == y[node])) dfs(i);
  }
}
int main() {
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> x[i] >> y[i];
  }
  for (int i = 1; i <= n; i++) {
    if (!vis[i]) {
      ans++;
      dfs(i);
    }
  }
  cout << ans - 1 << '\n';
  return 0;
}
