#include <bits/stdc++.h>
#include <math.h>
using namespace std;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep2(i, s, n) for (int i = (s); i < (int)(n); i++)

const int MOD = 1000000007;
const int INF_32 = 1LL << 30;
const int64_t INF_64 = 1LL << 60;

template <class T>
inline bool chmin(T& a, T b)
{
    if (a > b) {
        a = b;
        return true;
    }
    return false;
}
template <class T>
inline bool chmax(T& a, T b)
{
    if (a < b) {
        a = b;
        return true;
    }
    return false;
}

typedef long long ll;

#define MAX_N 10000
#define ORD_MONEY 1000000000000000

struct Edge {
    ll from;
    ll to;
    ll cost;
    Edge(ll f, ll t, ll c)
        : from(f)
        , to(t)
        , cost(c)
    {
    }
    bool operator<(const Edge& o) const
    {
        return cost < o.cost;
    }
};
using Graph = vector<vector<int>>;
using Graph_e = vector<vector<Edge>>;

vector<int> dx = { 1, 0, -1, 0 };
vector<int> dy = { 0, 1, 0, -1 };
typedef pair<int, int> P; // first:最短距離，second:頂点番号

typedef pair<ll, int> P_ll;

struct UnionFind {
    // par[i]:データiが属する木の親の番号．i==par[i]のとき，データiは木の根ノード
    vector<int> par;
    // sizes[i]:根ノードiの木に含まれるデータの数．iが根ノードでない場合は無意味な値となる
    vector<int> sizes;

    UnionFind(int n)
        : par(n)
        , sizes(n, 1)
    {
        // 最初はすべてのデータiがグループiに存在するものとして初期化
        rep(i, n) par[i] = i;
    }

    // データxが属する木の根を得る
    int find(int x)
    {
        if (x == par[x])
            return x;
        return par[x] = find(par[x]); // 根を張り替えながら再帰的に根ノードを探す
    }

    // 2つのデータx,yが属する木をマージ
    void unite(int x, int y)
    {
        //データの根ノードを得る
        x = find(x);
        y = find(y);

        // すでに同じ木に属しているならマージしない
        if (x == y)
            return;
        // xの木がyの木より大きくなるようにする
        if (sizes[x] < sizes[y])
            swap(x, y);

        // xがyの親になるように連結
        par[y] = x;
        sizes[x] += sizes[y];
        // sizes[y] = 0; // sizes[y]は無意味な値となるので0を入れておいてもよい
    }

    // 2つのデータx,yが属する木が同じならtrueを返す
    bool same(int x, int y)
    {
        return find(x) == find(y);
    }

    // データxが含まれる木の大きさを返す
    int size(int x)
    {
        return sizes[find(x)];
    }
};

int main()
{
    int N;
    cin >> N;
    vector<P_ll> x(N);
    vector<P_ll> y(N);
    rep(i, N)
    {
        ll x_input, y_input;
        cin >> x_input >> y_input;
        x.at(i).first = x_input;
        x.at(i).second = i;
        y.at(i).first = y_input;
        y.at(i).second = i;
    }

    sort(x.begin(), x.end());
    sort(y.begin(), y.end());

    vector<Edge> edges;
    for (int i = 1; i < N; i++) {
        int from, to;
        ll cost;
        from = x.at(i).second;
        to = x.at(i - 1).second;
        cost = x.at(i).first - x.at(i - 1).first;
        edges.push_back(Edge(from, to, cost));
    }
    for (int i = 1; i < N; i++) {
        ll from, to, cost;
        from = y.at(i).second;
        to = y.at(i - 1).second;
        cost = y.at(i).first - y.at(i - 1).first;
        edges.push_back(Edge(from, to, cost));
    }

    // コストが小さい順にソート
    sort(edges.begin(), edges.end());

    UnionFind uf(N);
    ll min_cost = 0;

    rep(ei, edges.size())
    {
        Edge& e = edges[ei];
        if (!uf.same(e.from, e.to)) {
            // 辺を追加しても閉路ができないなら，その辺を採用
            min_cost += e.cost;
            uf.unite(e.from, e.to);
        }
    }

    cout << min_cost << endl;
}


Measured memory_bytes: 11504