#include <bits/stdc++.h>
using namespace std;
#define rep(i,N) for(int i=0;i<int(N);++i)
#define rep1(i,N) for(int i=1;i<int(N);++i)
#define all(a) (a).begin(),(a).end()
#define print(v) { cerr<<#v<<": [ "; for(auto _ : v) cerr<<_<<", "; cerr<<"]"<<endl; }
#define printpair(v) { cerr<<#v<<": [ "; for(auto _ : v) cerr<<"{"<<_.first<<","<<_.second<<"}"<<", "; cerr<<"]"<<endl; }
#define dump(x) cerr<<#x<<": "<<x<<endl;
#define bit(k) (1LL<<(k))
typedef long long ll;
typedef pair<int, int> i_i;
typedef pair<ll, ll> l_l;
template <class T> using vec = vector<T>;
template <class T> using vvec = vector<vec<T>>;
template< typename T1, typename T2 >
ostream &operator<<(ostream &os, const pair< T1, T2 >& p) {
  os << "{" <<p.first << ", " << p.second << "}";
  return os;
}
template<class T> inline bool chmax(T& a, T b) { if (a < b) { a = b; return true; } return false; }
template<class T> inline bool chmin(T& a, T b) { if (a > b) { a = b; return true; } return false; }

const int INF = (ll)1e9+5;
const ll INFLL = (ll)1e18+1;
const ll MOD = (ll)1e9+7;
const double PI = acos(-1.0);
/*
const int dx[8] = {1, 0, -1, 0, 1, -1, -1, 1};
const int dy[8] = {0, 1, 0, -1, 1, 1, -1, -1};
const string dir = "DRUL";
*/

struct UnionFind{
    int n;
    vector<int> Parent;
    vector<int> sizes;
    UnionFind(int _n):n(_n),Parent(_n),sizes(_n,1){ rep(i,n)Parent[i]=i; }
    //find the root of x
    int root(int x){
        if(x!=Parent[x]){
        Parent[x] = root(Parent[x]);
        }
        return Parent[x];
    }
    //merge x and y
    void unite(int x,int y){
        x = root(x);
        y = root(y);
        if(x == y) return;
        if(sizes[x] < sizes[y]) swap(x, y);
        Parent[y] = x;
        sizes[x] += sizes[y];
    }
    bool same(int x,int y){ return root(x) == root(y); }
    int size(int x){ return sizes[root(x)]; }
    int group_num(){
        set<int> s;
        for(int i = 0; i < n; ++i){
            s.insert(root(i));
        }
        return int(s.size());
    }
};

/*~使い方講座~
まず、main関数内で 
    UnionFind uni(N);
と呼び出します（これでUnionFind木のuniちゃんが呼び出されます）。
uniちゃんの中には、NodeがN個あり、
それぞれの親とそれが属する集合の数（size）が定義されています。
    ①二つのNode xとy が同じ集合に属するか確認するとき
    uni.same(x, y);
    とすることで、それらが同じかどうかをtrue/falseで返す
    ②あるNode x が含まれる集合の大きさを知りたいとき
    uni.size(x);
    ③二つのNode xとyを同じ集合に属させたいとき
    uni.unite(x, y);
    ④あるNode xの根を知りたいとき
    uni.root(x);
*/
/*~UnionFind木が出題された問題~
①D-Decayed Bridges
https://beta.atcoder.jp/contests/abc120
UnionFind木は繋がりを断つことはできないが、
構成することはできるので、逆からやるやつ。
②D-Equals
https://beta.atcoder.jp/contests/abc097
swapすればそれらは行き来可能になるので、実質パイプ。
③B-Union Find
https://beta.atcoder.jp/contests/atc001
典型問題。uniteして、繋がってるか聞かれたら答えるだけ。
④B-道路工事
https://beta.atcoder.jp/contests/arc032/
すべての点に対して根を考えることで、
グループが幾つに分かれているかを知ることができる
⑤D-連結
https://beta.atcoder.jp/contests/abc049
二つのUnionFind木を管理して、
同時に繋がっていることを根のPairを持って
mapで管理すると良い
[解説]http://drken1215.hatenablog.com/entry/2019/06/29/182300
⑥D - Friend Suggestions
[問題]https://atcoder.jp/contests/abc157/tasks/abc157_d
[提出]https://atcoder.jp/contests/abc157/submissions/10460043
友達関係のグルーピングに使う
sizeも用いた
⑦E - 1 or 2
[問題]https://atcoder.jp/contests/abc126/tasks/abc126_e
[提出]https://atcoder.jp/contests/abc126/submissions/10649376
集合の数を管理するgroup_numを実装した
*/
struct Point{
    ll x, y, id;
    Point(ll x_, ll y_, ll id_):x(x_), y(y_), id(id_){};
};
struct edge{
    int x, y;
    ll cost;
    edge(ll x_, ll y_, ll cost_):x(x_), y(y_), cost(cost_){};
    bool operator < (const edge &e) const{
        return cost < e.cost;
    }
};

int main() {
    cin.tie(0);
    ios::sync_with_stdio(false);
    cout << fixed << setprecision(20);

    ll N;
    cin >> N;
    vector<Point> vx, vy;
    vx.emplace_back(-1, -1, -1);
    vx.emplace_back(INF, INF, -1);
    vy.emplace_back(-1, -1, -1);
    vy.emplace_back(INF, INF, -1);
    rep(i,N){
        ll x, y;
        cin >> x >> y;
        vx.emplace_back(x, y, i);
        vy.emplace_back(x, y, i);
    }
    auto x_comp = [](Point i, Point j){
        return i.x < j.x;
    };
    auto y_comp = [](Point i, Point j){
        return i.y < j.y;
    };
    sort(all(vx),x_comp);
    sort(all(vy),y_comp);
    // for(auto p: vx){
    //     cerr << "{" << p.x << ",　" << p.y << "}, ";
    // }
    // cerr << endl;
    // for(auto p: vy){
    //     cerr << "{" << p.x << ",　" << p.y << "}, ";
    // }
    // cerr << endl;
    vector<edge> Edges;
    rep1(i,N){
        auto f = vx[i];
        auto l = vx[i-1];
        auto r = vx[i+1];
        if(l.id != -1){
            Edges.emplace_back(f.id, l.id, abs(f.x - l.x));
        }
        if(r.id != -1){
            Edges.emplace_back(f.id, r.id, abs(f.x - r.x));
        }
    }
    rep1(i,N){
        auto f = vy[i];
        auto l = vy[i-1];
        auto r = vy[i+1];
        if(l.id != -1){
            Edges.emplace_back(f.id, l.id, abs(f.y - l.y));
        }
        if(r.id != -1){
            Edges.emplace_back(f.id, r.id, abs(f.y - r.y));
        }
    }
    sort(all(Edges));
    UnionFind uni(N);
    int sz = Edges.size();
    // rep(i,sz){
    //     cerr << "{" << Edges[i].x << "," << Edges[i].y << " " << Edges[i].cost << "}, ";
    // }
    // cerr << endl;
    ll ans = 0;
    rep(i,sz){
        if(not uni.same(Edges[i].x, Edges[i].y)){
            // dump(i);
            ans += Edges[i].cost;
            uni.unite(Edges[i].x, Edges[i].y);
        }
    }
    cout << ans << endl;
}


Measured memory_bytes: 18240