#include <bits/stdc++.h>
using namespace std;
#define rep(i,n) for (int i = 0; i < int(n); i++)
#define rep1(i,n) for (int i = 1; i <= int(n); i++)
#define repR(i,n) for (int i = int(n)-1; i >= 0; i--)
#define rep1R(i,n) for (int i = int(n); i >= 1; i--)
#define all(x) (x).begin(), (x).end()
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define fst first
#define snd second
#define cauto const auto
#ifdef LOCAL
#define dump(...) cerr << "[" << __LINE__ << ":" << __FUNCTION__ << "] ", _dmp_(#__VA_ARGS__, __VA_ARGS__);
void _dmp_(const char*) { cerr << endl; }
template <class T, class... U>
void _dmp_(const char *s, T const& x, U const& ...y) { const char *o = "({[", *e = "]})"; for (int i = 0; *s != '\0'; cerr << *s++) { if (count(o,o+3,*s)) i++; if (count(e,e+3,*s)) i--; if (!i && *s == ',') break; } cerr << " = " << x; if (*s == ',') cerr << ", ", s++; _dmp_(s, y...); }
#else
#define dump(...)
#endif
using ll = long long;
#define int ll
template <class T> using pque_max = priority_queue<T>;
template <class T> using pque_min = priority_queue<T, vector<T>, greater<T> >;
#define CONAINER_OUTPUT os << "{"; for (auto const& x : v) os << " " << x; return os << " }";
template <class T> ostream& operator<<(ostream& os, vector<T> const& v) { CONAINER_OUTPUT }
template <class T> ostream& operator<<(ostream& os, deque<T> const& v) { CONAINER_OUTPUT }
template <class T> ostream& operator<<(ostream& os, set<T> const& v) { CONAINER_OUTPUT }
template <class T, class U> ostream& operator<<(ostream& os, map<T,U> const& v) { CONAINER_OUTPUT }
#undef CONAINER_OUTPUT
template <class T> istream& operator>>(istream& is, vector<T>& v) { for (auto& x : v) is >> x; return is; }
template <class T, class S> ostream& operator<<(ostream& os, pair<T,S> const& p) { return os << "(" << p.first << ", " << p.second << ")"; }
template <class T, class S> istream& operator>>(istream& is, pair<T,S> const& p) { return is >> p.first >> p.second; }
struct _Init_ { _Init_() { cin.tie(nullptr); ios::sync_with_stdio(false); cout << fixed << setprecision(20); } } _init_;

template <class T> void quit(T const& x) { cout << x << endl; exit(0); }
template <class T> bool chmin(T& x, T const& y) { if (x > y) { x = y; return true; } return false; }
template <class T> bool chmax(T& x, T const& y) { if (x < y) { x = y; return true; } return false; }
#define YesNo(c) ((c) ? "Yes" : "No")
////////////////////////////////

struct UnionFind {
    int n; // id : 0...n-1
    vector<int> par, rank;

    UnionFind(int n) : n(n), par(n), rank(n) { rep (i,n) par[i] = i, rank[i] = 0; }
    int root(int x) {
        assert(0 <= x && x < n);
        return par[x] == x ? x : par[x] = root(par[x]);
    }
    bool unite(int x, int y) {
        x = root(x), y = root(y);
        if (x == y) return false;
        if (rank[x] < rank[y]) par[x] = y; else par[y] = x;
        if (rank[x] == rank[y]) rank[x]++;
        return true;
    }
    bool same(int x, int y) { return root(x) == root(y); }
};

int32_t main() {
    int n; cin >> n;
    struct town { int id,x,y; };
    vector<town> v(n);
    rep (i,n) {
        int x,y; cin >> x >> y;
        v[i].id = i; v[i].x = x; v[i].y = y;
    }

    pque_min<pair<int, pair<int, int> > > q;
    auto add_edge = [&](town const& s, town const& t) {
        q.push(mp(min(abs(s.x-t.x),abs(s.y-t.y)), mp(s.id, t.id)));
    };
    
    sort(all(v), [](town const& s, town const& t) { return s.x < t.x; });
    rep (i,n-1) add_edge(v[i],v[i+1]);
    sort(all(v), [](town const& s, town const& t) { return s.y < t.y; });
    rep (i,n-1) add_edge(v[i],v[i+1]);

    UnionFind uf(n);
    int c = 0, sum = 0;
    while (c < n-1) {
        cauto p = q.top(); q.pop();
        int x = p.snd.fst, y = p.snd.snd;
        if (uf.unite(x,y)) {
            c++;
            sum += p.fst;
        }
    }
    cout << sum << endl;
    
}


Measured memory_bytes: 10864