#include <bits/stdc++.h>
using namespace std;
using LL  = long long;
using VI  = vector<LL>;
using VVI = vector<VI>;
using VB  = vector<bool>;
using VS  = vector<string>;
using PII = pair<LL, LL>;
using VP  = vector<PII>;
#define PB           push_back
#define MP           make_pair
#define SZ(a)        LL((a).size())
#define EACH(x, c)   for (auto x : (c))
#define ALL(c)       (c).begin(), (c).end()
#define REVERSE(c)   reverse(ALL(c))
#define SORT(c)      stable_sort(ALL(c))
#define RSORT(c)     stable_sort((c).rbegin(), (c).rend())
#define FSORT(c)     stable_sort(ALL(c), [] (auto& x, auto& y) {return x.first < y.first;});
#define FRSORT(c)    stable_sort(ALL(c), [] (auto& x, auto& y) {return x.first > y.first;});
#define SSORT(c)     stable_sort(ALL(c), [] (auto& x, auto& y) {return x.second < y.second;});
#define SRSORT(c)    stable_sort(ALL(c), [] (auto& x, auto& y) {return x.second > y.second;});
#define FOR(i, a, b) for (LL i = (a); i < (b); ++i)
#define REP(i, n)    FOR(i, 0, n)
#define DEBUG true
#define $(x) {if (DEBUG) {cout << #x << " = " << (x) << endl;}}

class UnionFind {
public:
    VI parent;
    UnionFind(LL N) : parent(N) {
        iota(ALL(parent), 0);   // 初期化(親==自分)
    }
    LL getRoot(LL x) {   // 根に到達するまで親を辿る
        if (parent[x] == x) return x;   // 根は親==自分
        return parent[x] = getRoot(parent[x]);   // 経路圧縮(辿った要素を全て根に直結させる)
    }
    bool inSameSet(LL x, LL y) {   // 根が同じなら同じ集合に属する
        return getRoot(x) == getRoot(y);
    }
    void unite(LL x, LL y) {   // 同値関係 x~y を追加(xが属する集合とyが属する集合をマージ)
        x = getRoot(x);
        y = getRoot(y);
        if (x == y) return;   // すでに同じ集合
        parent[x] = y;
    }
};

struct Edge {
    LL source, target, weight;
};
class Graph {
public:
    LL first_node;
    VVI nodes;
    vector<Edge> edges;
    Graph(LL N, LL first = 1) : nodes(N + first), first_node(first) {}
    void addEdge(LL s, LL t, LL w) {
        nodes[s].PB(t);
        nodes[t].PB(s);   // delete this line for directed graph
        edges.PB(Edge{s, t, w});
        //edges.PB(Edge{t, s, w});   // delete this line for directed graph
    }
    void show() {
        EACH(edge, edges) {
            cout << edge.source << " -> " << edge.target << " : " << edge.weight << endl;
        }
    }
    LL kruskal() {
        UnionFind uf(SZ(nodes));
        sort(ALL(edges), [] (auto& x, auto& y) {return x.weight < y.weight;});
        LL weight_sum = 0;
        EACH(e, edges) {
            if (!uf.inSameSet(e.source, e.target)) {
                uf.unite(e.source, e.target);
                weight_sum += e.weight;
            }
        }
        return weight_sum;
    }
};

struct Tuple {
    LL index;   // ソート前のインデックス
    LL x, y;   // ソートしたい中身
};

int main() {
    LL N;
    cin >> N;
    vector<Tuple> T(N);
    REP(i, N) {
        LL x, y;
        cin >> x >> y;
        T[i] = Tuple{i, x, y};
    }

    Graph G(N, 0);
    sort(ALL(T), [] (auto& x, auto& y) {return x.x < y.x;});
    REP(i, N - 1) {
        G.addEdge(T[i].index, T[i + 1].index, T[i + 1].x - T[i].x);
    }
    sort(ALL(T), [] (auto& x, auto& y) {return x.y < y.y;});
    REP(i, N - 1) {
        G.addEdge(T[i].index, T[i + 1].index, T[i + 1].y - T[i].y);
    }
    //G.show();

    cout << G.kruskal() << endl;
    
    return 0;
}


Measured memory_bytes: 17328