#include <stdio.h>
#include <algorithm>
#include <utility>
#include <functional>
#include <cstring>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
#include <iterator>
#include <vector>
#include <string>
#include <set>
#include <iostream>
#include <random>
#include <map>
#include <iomanip>
#include <list>
#include <typeinfo>
#include <list>
#include <set>
#include <cassert>
#include <fstream>
#include <unordered_map>
#include <complex>
#include <cctype>
#include <bitset>
using namespace std;
using ll = long long;
using pll = pair<long long, long long>;
using vll = vector<long long>;
#define rep(i,n) for(long long i(0);(i)<(n);(i)++)
ll INF = 1LL << 60;

struct edge{
    ll to;
    ll cost;
    edge(ll t, ll c){
        to = t;
        cost = c;
    }
};
struct fulledge{
    ll u, v, cost;
    
    fulledge(ll _u, ll _v, ll _c){
        u = _u; v = _v; cost = _c;
    }

};

struct edgeGreater{
    bool operator()(const fulledge &e1, const fulledge &e2){
        return e1.cost > e2.cost;
    }
};

ll Prim(vector<vector<edge> > &G, ll s){
    ll N = G.size(), total_cost = 0;
    vector<bool> used(N);
    priority_queue<fulledge, vector<fulledge>, edgeGreater> que;
    //* initialize heap
    for(ll i=0; i< G[s].size(); i++){
        que.push(fulledge(s, G[s][i].to, G[s][i].cost));
    }
    used[s] = true;
    //* 
    while(!que.empty()){
        fulledge e = que.top(); que.pop();
        if(used[e.u] && used[e.v]) 
            continue;
        //* now e is in the fromtier
        total_cost += e.cost;
        ll u = e.u; if(used[u]) u = e.v; 
        used[u] = true;
        //* scan through u's neighbours
        for(ll i=0; i< G[u].size(); i++){
            if(used[G[u][i].to]) continue;
            //* push new edges into que
            que.push(fulledge(u, G[u][i].to, G[u][i].cost));
        }
    }
    return total_cost;
}

ll minDist(pll a, pll b){
    return min(abs(a.first-b.first), abs(a.second-b.second));
}

void solve(long long N, std::vector<long long> x, std::vector<long long> y){
    vector<pair<pll, ll> > list(N), rlist(N);
    rep(i, N){
        list[i].first = pll(x[i], y[i]); list[i].second = i;
        rlist[i].first = pll(y[i], x[i]); rlist[i].second = i;
    }

    sort(list.begin(),list.end());
    sort(rlist.begin(), rlist.end());
    //* build sufficient sub-graph
    vector<vector<edge> > G(N);
    rep(i, N){
        pll p1, p2;
        ll v1, v2, cost;
        if(i > 0){
            p1 = list[i].first; p2 = list[i-1].first;
            v1 = list[i].second; v2 = list[i-1].second;
            cost = minDist(p1, p2);
            G[v1].push_back(edge(v2, cost));
            G[v2].push_back(edge(v1, cost));

            p1 = rlist[i].first; p2 = rlist[i-1].first;
            v1 = rlist[i].second; v2 = rlist[i-1].second;
            cost = minDist(p1, p2);
            G[v1].push_back(edge(v2, cost));
            G[v2].push_back(edge(v1, cost));
        }
        else if(i < N-1){
            p1 = list[i].first; p2 = list[i+1].first;
            v1 = list[i].second; v2 = list[i+1].second;
            cost = minDist(p1, p2);
            G[v1].push_back(edge(v2, cost));
            G[v2].push_back(edge(v1, cost));

            p1 = rlist[i].first; p2 = rlist[i+1].first;
            v1 = rlist[i].second; v2 = rlist[i+1].second;
            cost = minDist(p1, p2);
            G[v1].push_back(edge(v2, cost));
            G[v2].push_back(edge(v1, cost));
        }
    }

    ll total_cost = Prim(G, 0);
    cout << total_cost << endl;

}

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: 21880