#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i, n) for (ll i = 0; i < n; ++i)
#define P pair<ll, ll>
#define Graph vector<vector<ll>>
#define fi first
#define se second
constexpr ll mod = 1000000007;
constexpr ll INF = (1ll << 60);
constexpr double pi = 3.14159265358979323846;
template <typename T>
inline bool chmax(T &a, T b)
{
    if (a < b)
    {
        a = b;
        return 1;
    }
    return 0;
}
template <typename T>
inline bool chmin(T &a, T b)
{
    if (a > b)
    {
        a = b;
        return 1;
    }
    return 0;
}

//閉路判定でUnionfindを使う

/* UnionFind：素集合系管理の構造体(union by rank)
    isSame(x, y): x と y が同じ集合にいるか。 計算量はならし O(α(n))
    unite(x, y): x と y を同じ集合にする。計算量はならし O(α(n))
*/
struct UnionFind
{ // The range of node number is u 0 v n-1
    vector<ll> rank, parents;
    UnionFind() {}
    UnionFind(ll n)
    { // make n trees.
        rank.resize(n, 0);
        parents.resize(n, 0);
        for (ll i = 0; i < n; i++)
        {
            makeTree(i);
        }
    }
    void makeTree(ll x)
    {
        parents[x] = x; // the parent of x is x
        rank[x] = 0;
    }
    bool isSame(ll x, ll y) { return findRoot(x) == findRoot(y); }
    void unite(ll x, ll y)
    {
        x = findRoot(x);
        y = findRoot(y);
        if (rank[x] > rank[y])
        {
            parents[y] = x;
        }
        else
        {
            parents[x] = y;
            if (rank[x] == rank[y])
            {
                rank[y]++;
            }
        }
    }
    ll findRoot(ll x)
    {
        if (x != parents[x])
            parents[x] = findRoot(parents[x]);
        return parents[x];
    }
};
// 辺の定義
struct Edge
{
    long long u;
    long long v;
    long long cost;
};
bool comp_e(const Edge &e1, const Edge &e2) { return e1.cost < e2.cost; } // 辺を直接比較するための関数
/* Kruskal :クラスカル法で minimum spanning tree を求める構造体
    入力: 辺のvector, 頂点数V
    最小全域木の重みの総和: sum
    計算量: O(|E|log|V|)
*/
struct Kruskal
{
    UnionFind uft;
    long long sum; // 最小全域木の重みの総和
    vector<Edge> edges;
    ll V;
    Kruskal(const vector<Edge> &edges_, ll V_) : edges(edges_), V(V_) { init(); }
    void init()
    {
        sort(edges.begin(), edges.end(), comp_e); // 辺の重みでソート
        uft = UnionFind(V);
        sum = 0;
        for (auto e : edges)
        {
            if (!uft.isSame(e.u, e.v))
            { // 閉路にならなければ加える
                uft.unite(e.u, e.v);
                sum += e.cost;
            }
        }
    }
};

int main(){
    /*ll V,E;cin>>V>>E;
    vector<Edge> edges(E);
    rep(i,E){
        ll s,t,w;cin>>s>>t>>w;
        Edge e={s,t,w};
        edges[i]=e;
    }
    Kruskal k(edges,V);
    cout<<k.sum<<endl;*/

    ll n;
    cin >> n;
    vector<P> x(n), y(n);
    vector<Edge> edges;
    rep(i, n)
    {
        cin >> x[i].fi >> y[i].fi;
        x[i].se = i;
        y[i].se = i;
    }
    sort(x.begin(), x.end());
    sort(y.begin(), y.end());

    rep(i, n - 1)
    {
        ll xdi, ydi, edge1_x, edge2_x, edge1_y, edge2_y;
        if (i == 0)
        {
            xdi = abs(x[i].fi - x[i + 1].fi);
            ydi = abs(y[i].fi - y[i + 1].fi);

            edges.push_back({x[i].se, x[i + 1].se, xdi});
            edges.push_back({y[i].se, y[i + 1].se, ydi});
        }
        else
        {
            edge1_x = x[i].se;
            edge2_x = (abs(x[i].fi - x[i + 1].fi) > abs(x[i].fi - x[i - 1].fi) ? x[i - 1].se : x[i + 1].se);
            edge1_y = y[i].se;
            edge2_y = (abs(y[i].fi - y[i + 1].fi) > abs(y[i].fi - y[i - 1].fi) ? y[i - 1].se : y[i + 1].se);

            edges.push_back({x[i].se, x[i + 1].se, abs(x[i].fi - x[i + 1].fi)});
            edges.push_back({x[i].se, x[i - 1].se, abs(x[i].fi - x[i - 1].fi)});
            edges.push_back({y[i].se, y[i + 1].se, abs(y[i].fi - y[i + 1].fi)});
            edges.push_back({y[i].se, y[i - 1].se, abs(y[i].fi - y[i - 1].fi)});
        }
    }

    ll V = edges.size();
    Kruskal krs(edges, V);
    cout << krs.sum << endl;

    return 0;
}

Measured memory_bytes: 31304