// Original C++ solution (slower):
#include<bits/stdc++.h>

using namespace std;

#define FOR(i,l,r) for(long long i=(l);i<(r);++i)

#define REP(i,n) FOR(i,0,n)

#define REPS(i,n) FOR(i,1,n+1)

#define RFOR(i,l,r) for(long long i=(l);i>=(r);--i)

#define RREP(i,n) RFOR(i,n-1,0)

#define RREPS(i,n) RFOR(i,n,1)

#define int long long

#define mp make_pair

#define pb push_back

#define eb emplace_back

#define SZ(x) ((int)(x).size())

#define all(x) (x).begin(),(x).end()

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;

}



const int INF=1e18;

const int MOD=1e9+7;

const int MAX_N = 1e5;



int n;

struct Edge{

    int cost,to;

    Edge(int cost,int to):cost(cost),to(to){}

    bool operator<(const Edge& a) const{

        return cost >a.cost;

    }

};

using Graph=vector<Edge>;

Graph G[MAX_N];

bool used[MAX_N];

int res=0;



int prim(){

    REP(i,n)used[i]=false;

    used[0]=true;

    priority_queue<Edge>q;

    for(auto v:G[0]){

        q.push(v);

    }

    while(q.size()){

        Edge d=q.top();q.pop();

        if(used[d.to])continue;

        res+=d.cost;

      	used[d.to]=true;

        for(auto v:G[d.to]){

            if(!used[v.to])q.push(v);

        }

    }

    return res;

}

typedef pair<int,int> P;



signed main(){

    cin >> n;

    vector<P> x(n),y(n);

    REP(i,n){

        int a, b; cin >> a >> b;

        x[i] = {a,i};

        y[i] = {b,i};

    }

    sort(all(x));

    sort(all(y));

    REP(i,n-1){

        int cx = abs(x[i].first-x[i+1].first);

        int i1 = x[i].second;

        int i2 = x[i+1].second;

        int cy = abs(y[i].first-y[i+1].first);

        int i3 = y[i].second;

        int i4 = y[i+1].second;

        G[i1].pb({cx,i2});

        G[i2].pb({cx,i1});

        G[i3].pb({cy,i4});

        G[i4].pb({cy,i3});

    }

    cout << prim() << endl;

}

// Optimized solution to the same problem (faster):
#include<bits/stdc++.h>

using namespace std;

#define ll long long

#define FOR(i,l,r) for(ll i=(l);i<(r);++i)

#define REP(i,n) FOR(i,0,n)

#define REPS(i,n) FOR(i,1,n+1)

#define RFOR(i,l,r) for(ll i=(l);i>=(r);--i)

#define RREP(i,n) RFOR(i,n-1,0)

#define RREPS(i,n) RFOR(i,n,1)

#define pb push_back

#define eb emplace_back

#define SZ(x) ((ll)(x).size())

#define all(x) (x).begin(),(x).end()

#define rall(x) (x).rbegin(),(x).rend()

template<class T = ll> using V = vector<T>;

template<class T = ll> using VV = V<V<T>>;

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; }

inline void Yes(bool b = true) {cout << (b ? "Yes" : "No") << '\n';}

inline void YES(bool b = true) {cout << (b ? "YES" : "NO") << '\n';}

inline void err(bool b = true) {if(b) {cout << -1 << '\n'; exit(0);}}

template<class T> inline void fin(bool b = true, T e = 0) {if(b) {cout << e << '\n'; exit(0);}}

template<class T> T Roundup_div(T x, T y) {return (x+(y-1))/y;}

const ll INF = 1e18;

template <typename T>

T pow(T a, long long n, T e = 1) {

    T ret = e;

    while (n) {

        if (n & 1) ret *= a;

        a *= a;

        n >>= 1;

    }

    return ret;

}



struct UnionFind {

    public:

    vector<int> par;

    UnionFind(int n){

        par=vector<int>(n,-1);

    }

    int root(int x){

        if(par[x]<0)return x;

    return par[x]=root(par[x]);

    }

    bool issame(int x,int y){

        return root(x)==root(y);

    }

    bool merge(int x,int y){

        x=root(x);

        y=root(y);

        if(x==y)return false;

        if(par[x]>par[y])swap(x,y);

        par[x]+=par[y];

        par[y]=x;

        return true;

    }

    int size(int x){

        return -par[root(x)];

    }

};



struct Edge{

    ll from, to, cost;

    Edge(ll from, ll to,ll cost):from(from), to(to), cost(cost){}

    bool operator < (const Edge& a) const{

        return cost < a.cost;

    }

};

using Edges = vector<Edge>;

Edges E;

 

ll kruscal(ll n, Edges E){

    ll res = 0;

    sort(all(E));

    UnionFind uf(n);

    for(auto v:E) if(uf.merge(v.from, v.to)) res += v.cost; 

    return res;

}



using P = pair<ll, ll>;



int main(){

    ios::sync_with_stdio(false);

    cin.tie(nullptr);

    int n; cin >> n;

    V<P> xv(n), yv(n);

    REP(i, n){

        ll x, y; cin >> x >> y;

        xv[i] = {x, i};

        yv[i] = {y, i};

    }

    sort(rall(xv));

    sort(rall(yv));

    REP(i, n-1){

        E.pb({xv[i].second, xv[i+1].second, xv[i].first - xv[i+1].first});

        E.pb({yv[i].second, yv[i+1].second, yv[i].first - yv[i+1].first});

    }

    cout << kruscal(n, E) << '\n';

}

// Fastest known solution for this problem: