#include <bits/stdc++.h>
#define BIT(n) (1LL << (n))
#define REP(i, n) for (int i = 0; i < n; i++)
#define REPB(i, n) for (int i = 0; i < BIT(n); i++)
#define REPS(i, x) for (int i = 1; i <= (int)(x); i++)
#define REPR(i, n) for (int i = n; i >= 0; i--)
#define FOR(i, m, n) for (int i = m; i < n; i++)
#define FORA(i, n) for (auto &&i : n)
#define FORS(i, m, n) for (int i = m; i <= n; i++)
#define DEBUG(x) cerr << #x << ": " << x << "\n"
#define PRINTALL(V)                                                            \
  for (auto v : (V)) {                                                         \
    cerr << v << " ";                                                          \
  }                                                                            \
  cerr << "\n";
#define ALL(v) v.begin(), v.end()
#define fst first
#define snd second
#define mp make_pair
#define pb push_back
#define epb emplace_back
#define int long long
using namespace std;
template < class T > bool chmax(T &a, const T &b) {
  if (a < b) {
    a = b;
    return 1;
  }
  return 0;
}
template < class T > bool chmin(T &a, const T &b) {
  if (a > b) {
    a = b;
    return 1;
  }
  return 0;
}
struct UnionFind {
  vector< int > par; // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2
  vector< int > siz;
  int parsize;

  UnionFind(int N) : par(N) { //最初は全てが根であるとして初期化
    for (int i = 0; i < N; i++)
      par[i] = i;
    siz.assign(N, 1);
    parsize = N;
  }

  int root(int x) { // データxが属する木の根を再帰で得る：root(x) = {xの木の根}
    if (x < 0 || parsize <= x)
      return -1;
    if (par[x] == x)
      return x;
    return par[x] = root(par[x]);
  }

  bool unite(int x, int y) { // xとyの木を併合
    if (x < 0 || parsize <= x || y < 0 || parsize <= y)
      return false;
    int rx = root(x); // xの根をrx
    int ry = root(y); // yの根をry
    if (rx == ry)
      return false; // xとyの根が同じ(=同じ木にある)時はそのまま
    if (siz[x] < siz[y])
      swap(x, y);
    siz[x] += siz[y];
    par[ry] =
        rx; // xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
    return true;
  }

  bool same(int x, int y) { // 2つのデータx, yが属する木が同じならtrueを返す
    if (x < 0 || parsize <= x || y < 0 || parsize <= y)
      return false;
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
  int size(int x) { return (x < 0 || parsize <= x) ? -1 : siz[root(x)]; }
};
template < class T > using vec = std::vector< T >;
template < class T > void print(const T &x) { cout << x << "\n"; }
const int MOD = 1000000007, INF = 1061109567;
const double EPS = 1e-10, PI = acos(-1.0);
const int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
signed main() {
  cin.tie(0), ios::sync_with_stdio(false);
  cout << fixed << setprecision(10);
  int N;
  cin >> N;

  map< int, int > mpx;
  map< int, int > mpy;
  UnionFind tree(N);

  REP(i, N) {
    int x, y;
    cin >> x >> y;

    if (mpx[x] != 0) {
      tree.unite(mpx[x] - 1, i);
    } else {
      mpx[x] = i + 1;
    }
    if (mpy[y] != 0) {
      tree.unite(mpy[y] - 1, i);
    } else {
      mpy[y] = i + 1;
    }
  }

  // DEBUG(counter);
  vec< vec< int > > edge;
  pair< int, int > prev = mp(-INF, -INF);

  FORA(e, mpx) {
    vec< int > tmp = {e.first - prev.first, e.second, prev.second};
    edge.pb(tmp);
    prev = e;
  }
  prev = mp(-INF, -INF);
  FORA(e, mpy) {
    vec< int > tmp = {e.first - prev.first, e.second, prev.second};
    edge.pb(tmp);
    prev = e;
  }

  sort(ALL(edge));
  // PRINTALL(edge[0]);
  int ans = 0;
  FORA(e, edge) {
    if (tree.unite(e[1] - 1, e[2] - 1)) {
      ans += e[0];
    }
  }
  print(ans);
}

Measured memory_bytes: 26668