#include <bits/stdc++.h>
#define rep(i, a, n) for (int i = (int)(a); i <= (int)(n); ++i)
#define rrep(i, a, n) for (int i = (int)(a); i >= (int)(n); --i)
#define debug(x) cerr << #x << " = " << x << "\n"
#define debugv(x) \
  rep(f, 0, (x.size() - 1)) cerr << x[f] << (f == (x.size() - 1) ? "\n" : " ")
#define all(x) x.begin(), x.end()
#define int long long
using namespace std;
typedef pair<int, int> pii;
typedef vector<int> vi;
const int MX = 1e5 + 5, INF = 5 << 28, MOD = 1e9 + 7;

struct UnionFind {
  vector<int> par;  // par[i]:iの親の番号　(例) par[3] = 2 : 3の親が2

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

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

  void unite(int x, int y) {  // xとyの木を併合
    int rx = root(x);         // xの根をrx
    int ry = root(y);         // yの根をry
    if (rx == ry) return;  // xとyの根が同じ(=同じ木にある)時はそのまま
    par[rx] =
        ry;  // xとyの根が同じでない(=同じ木にない)時：xの根rxをyの根ryにつける
  }

  bool same(int x, int y) {  // 2つのデータx, yが属する木が同じならtrueを返す
    int rx = root(x);
    int ry = root(y);
    return rx == ry;
  }
};
int N;
vector<pii> vx, vy;
void input() {
  cin >> N;
  vx.resize(N);
  vy.resize(N);
  rep(i, 0, N - 1) {
    int x, y;
    cin >> x >> y;
    vx[i] = (pii(x, i));
    vy[i] = (pii(y, i));
  }
}

const int MAX_V = 201010;

struct edge {
  int u, v, cost;
};
vector<edge> es;
vector<pii> G[MAX_V];
bool used[MAX_V] = {};
int mincost[MAX_V] = {};
void solve() {
  sort(all(vx));
  sort(all(vy));

  rep(i, 1, vx.size() - 1) {
    edge e;
    e.u = vx[i - 1].second;
    e.v = vx[i].second;
    e.cost = abs(vx[i].first - vx[i - 1].first);
    // es.push_back(vx[i - 1].second, vx[i].second,
    //              abs(vx[i].first - vx[i - 1].first));
    es.push_back(e);

    // G[vx[i - 1].second].push_back(
    //     pii(vx[i].second, abs(vx[i].first - vx[i - 1].first)));

    // G[vx[i].second].push_back(
    //     pii(vx[i - 1].second, abs(vx[i].first - vx[i - 1].first)));
  }
  rep(i, 1, vy.size() - 1) {
    edge e;
    e.u = vy[i - 1].second;
    e.v = vy[i].second;
    e.cost = abs(vy[i].first - vy[i - 1].first);
    es.push_back(e);
    // es.push_back(vx[i - 1].second, vx[i].second,
    //              abs(vx[i].first - vx[i - 1].first));
    // G[vy[i - 1].second].push_back(
    //     pii(vy[i].second, abs(vy[i].first - vy[i - 1].first)));
    // G[vy[i].second].push_back(
    //     pii(vy[i - 1].second, abs(vy[i].first - vy[i - 1].first)));
  }
  sort(all(es), [](const edge &a, const edge &b) { return (a.cost < b.cost); });

  UnionFind uf(N);
  int ans = 0;
  for (int i = 0; i < es.size(); ++i) {
    edge e = es[i];
    if (!uf.same(e.u, e.v)) {
      uf.unite(e.u, e.v);
      ans += e.cost;
    }
  }

  // rep(i, 0, MAX_V - 1) mincost[i] = INF;

  // mincost[0] = 0;
  // int ans = 0;
  // int V = N;

  // while (true) {
  //   int v = -1;
  //   for (int u = 0; u < V; ++u) {
  //     if (!used[u] && (v == -1 || mincost[u] < mincost[v])) {
  //       v = u;
  //     }
  //   }
  //   if (v == -1) break;
  //   used[v] = true;
  //   ans += mincost[v];
  //   // vを追加した．
  //   for (auto x : G[v]) {
  //     mincost[x.first] = min(mincost[x.first], x.second);
  //   }
  // }
  cout << ans << endl;
}
signed main() {
  input();
  solve();
  return 0;
}

Measured memory_bytes: 16240