#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define int ll

#define REP(i,n) for(int i=0;i<n;++i)
#define ALL(name) name.begin(),name.end()
#define SORT(name) sort(name.begin(), name.end())
#define ZERO(p) memset(p, 0, sizeof(p))
#define MINUS(p) memset(p, -1, sizeof(p))
#if 1
#  define DBG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#  define DBG(fmt, ...)
#endif

const ll LLINF = (1LL<<60);
const int INF = (1LL<<30);
const double DINF = std::numeric_limits<double>::infinity();
const int MOD = 1000000007;
#define MAX_N 100010

class UnionFind {
    public:
        UnionFind(int size) { m_Data.assign(size, -1); }
        bool Unite(int x, int y) {
            x = Find(x), y = Find(y);
            if(x == y) { return (false); }
            if(m_Data[x] > m_Data[y]) { swap(x, y); }
            m_Data[x] += m_Data[y];
            m_Data[y] = x;
            return (true);
        }
        int Find(int k) {
            if(m_Data[k] < 0) { return (k); }
            return (m_Data[k] = Find(m_Data[k]));
        }
        int Size(int k) { return (-m_Data[Find(k)]); }
    private:
        vector<int> m_Data;
};

// ※要 UnionFind
// T : コストの型
template<typename T>
class Kruskal {
    public:
        // 辺を表す構造体
        struct Edge {
            int src, to;  // src: 指し元, to: 指し先
            T cost;       // 辺のコスト
            Edge(int to, T cost) : src(-1), to(to), cost(cost) {}
            Edge(int src, int to, T cost) : src(src), to(to), cost(cost) {}
            Edge &operator=(const int &x) {
                to = x;
                return *this;
            }
            operator int() const { return to; }
        };
    public:
        Kruskal() {}
        Kruskal(int v) { Init(v); }
        void Init(int v) { m_V = v; }
        // 有向辺の追加 // TODO: これいる？
        void AddDEdge(int from, int to, T cost) {
            m_Edges.push_back(Edge(from, to, cost));
        }
        // 無向辺の追加(※0-indexed でなければいけない)
        void AddUdEdge(int from, int to, int cost) {
            m_Edges.push_back(Edge(from, to, cost));
            m_Edges.push_back(Edge(to, from, cost));
        }
        // 全域木の最小コストを返す
        T GetCost() {
            sort(begin(m_Edges), end(m_Edges), [](const Edge &a, const Edge &b) {
                    return (a.cost < b.cost);
                    });
            UnionFind tree(m_V);
            T ret = 0;
            for(auto &e : m_Edges) { if(tree.Unite(e.src, e.to)) { ret += e.cost; } }
            return (ret);
        }
    private:
        int m_V;                // 頂点数
        vector<Edge> m_Edges;   // 辺集合
};

int N;
vector< tuple<int, int, int> > XY, YX;

signed main() {
    cin.tie(0); cout.tie(0);
    ios::sync_with_stdio(false);

    cin >> N;
    REP(i, N) {
        int x, y;
        cin >> x >> y;
        XY.push_back(make_tuple(x, y, i));
        YX.push_back(make_tuple(y, x, i));
    }
    SORT(XY);
    SORT(YX);

    Kruskal<int> kr(N);

    REP(i, N) {
        for(int j = i + 1; j < min(N, i + 10); ++j) {
            int x1 = get<0>(XY[i]);
            int y1 = get<1>(XY[i]);
            int ind1 = get<2>(XY[i]);
            int x2 = get<0>(XY[j]);
            int y2 = get<1>(XY[j]);
            int ind2 = get<2>(XY[j]);

            int dist = min(abs(x1 - x2), abs(y1 - y2));
            //DBG("%lld %lld %lld %lld %lld\n", x1, y1, x2, y2, dist);
            kr.AddUdEdge(ind1, ind2, dist);
        }
        for(int j = i + 1; j < min(N, i + 10); ++j) {
            int x1 = get<1>(YX[i]);
            int y1 = get<0>(YX[i]);
            int ind1 = get<2>(YX[i]);
            int x2 = get<1>(YX[j]);
            int y2 = get<0>(YX[j]);
            int ind2 = get<2>(YX[j]);

            int dist = min(abs(x1 - x2), abs(y1 - y2));
            kr.AddUdEdge(ind1, ind2, dist);
        }
    }

    int ans = kr.GetCost();
    printf("%lld\n", ans);

    return 0;
}


Measured memory_bytes: 105620