#include <bits/stdc++.h>
using namespace std;

struct UnionFind {
public:
    explicit UnionFind(int n)
        : roots(n)
        , sizes(n, 1)
    {
        for (int i = 0; i < n; i++) {
            roots[i] = i;
        }
    }

    // 頂点xの属する木の根である頂点を探す
    int find(int x) const
    {
        if (x == roots[x]) {
            return x;
        }
        roots[x] = find(roots[x]);
        return roots[x];
    }

    // 頂点x、頂点yが属する木を連結する
    void unite(int x, int y)
    {
        // 頂点x、頂点yのそれぞれの木の根rx、ryを得る
        auto rx = find(x);
        auto ry = find(y);

        // すでに同じ木かどうかチェック
        if (rx == ry) {
            return;
        }

        // 大きい方の木の下に小さい方の木を連結するようにする
        // (rxのほうが小さい時はrxとryを入れ替える)
        if (sizes[rx] < sizes[ry]) {
            swap(rx, ry);
        }

        // ryをrxに属するようにする
        roots[ry] = rx;
        sizes[rx] += sizes[ry];
    }

    // 頂点xと頂点yが同じ木に属するならtrue、そうでなければfalseを返す
    bool same(int x, int y) const
    {
        return find(x) == find(y);
    }

    // 頂点xが属する木の大きさを返す
    int size(int x) const
    {
        return sizes[find(x)];
    }

private:
    mutable vector<int> roots; // 頂点iの属する木の根を持つ(roots[i]が頂点iの属する木の根)
    vector<int> sizes; // 木の根iに属する頂点の数(木のサイズ)
};

struct town {
    long long no;
    long long x;
    long long y;
};

struct edge {
    long long cost;
    long long town1;
    long long town2;
};

void solve(long long N, std::vector<long long> x, std::vector<long long> y)
{
    std::vector<shared_ptr<town>> towns;
    for (long long i = 0; i < N; i++) {
        auto t = make_shared<town>();
        t->no = i + 1;
        t->x = x[i];
        t->y = y[i];
        towns.push_back(move(t));
    }

    vector<shared_ptr<edge>> edges;

    sort(begin(towns), end(towns), [](auto&& t1, auto&& t2) { return t1->x < t2->x; });
    for (long long i = 0; i < N - 1; i++) {
        auto e = make_shared<edge>();
        e->cost = towns[i + 1]->x - towns[i]->x;
        e->town1 = towns[i]->no;
        e->town2 = towns[i + 1]->no;
        edges.push_back(move(e));
    }

    sort(begin(towns), end(towns), [](auto&& t1, auto&& t2) { return t1->y < t2->y; });
    for (long long i = 0; i < N - 1; i++) {
        auto e = make_shared<edge>();
        e->cost = towns[i + 1]->y - towns[i]->y;
        e->town1 = towns[i]->no;
        e->town2 = towns[i + 1]->no;
        edges.emplace_back(move(e));
    }

    sort(begin(edges), end(edges), [](auto&& e1, auto&& e2) { return e1->cost < e2->cost; });

    UnionFind uf(N + 1);
    long long ans = 0;
    for (auto&& e : edges) {
        if (!uf.same(e->town1, e->town2)) {
            ans += e->cost;
            uf.unite(e->town1, e->town2);
        }
        if (uf.size(1) == N) {
            break;
        }
    }

    cout << ans << endl;
}

// Generated by 1.1.7.1 https://github.com/kyuridenamida/atcoder-tools  (tips: You use the default template now. You can remove this line by using your custom template)
int main()
{
    long long N;
    scanf("%lld", &N);
    std::vector<long long> x(N);
    std::vector<long long> y(N);
    for (int i = 0; i < N; i++) {
        scanf("%lld", &x[i]);
        scanf("%lld", &y[i]);
    }
    solve(N, std::move(x), std::move(y));
    return 0;
}


Measured memory_bytes: 22252