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

typedef long long ll; // -9,223,372,036,854,775,808 から 9,223,372,036,854,775,807

template <class T> using vec = std::vector<T>;
template <class T> using vec2 = vec<vec<T>>;
template <class T> using vec3 = vec<vec<vec<T>>>;

#define DEBUG

struct Town{
    ll x;
    ll y;
    int id;
};

struct Road{
    int from;
    int to;
    ll d;
};

class UnionFind{
    private:
    vector<int> par; // 親
    vector<int> rank; // 木の深さ
    vector<long long> sizes_; 
    int num_of_conn;

    public:
    // n要素で初期化
    UnionFind(int n) {
        par = vector<int>(n);
        rank = vector<int>(n);
        sizes_ = vector<long long>(n);
        for (int i = 0; i < n; i++) {
            par[i] = i;
            rank[i] = 0;
            sizes_[i] = 1;
        }
        num_of_conn = n;
    }

    // 木の根を求める
    int find(int x) {
        if (par[x] == x) {
            return x;
        } else {
            return par[x] = find(par[x]);
        }
    }

    long long getSize(int x){
        return sizes_[par[x]];
    }

    // xとyの属する集合を併合
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        auto sizex = getSize(x);
        auto sizey = getSize(y);
        if (x == y) return;
        if (rank[x] < rank[y]) {
            par[x] = y;
        } else {
            par[y] = x;
            if (rank[x] == rank[y]) rank[x]++;
        }
        num_of_conn--;
        sizes_[find(x)] = sizex + sizey;
    }

    // xとyが同じ集合に属するか否か
    bool same(int x, int y) {
        return find(x) == find(y);
    }

    int numOfConnected(){
        return num_of_conn;
    }
};


void solve(long long N, std::vector<long long> x, std::vector<long long> y){
    vec<Town> towns(N);
    for(int i=0; i < N; i++){
        towns[i] = Town{x[i], y[i], i};
    }

    vec<Road> roads;

    sort(towns.begin(), towns.end(), [](Town a, Town b){return a.x < b.x;});  // aがbより先頭にくるときtrueになるように。
    for(int i=0; i < towns.size() - 1; i++){
        roads.push_back(Road{towns[i].id, towns[i+1].id, towns[i+1].x - towns[i].x});
    }

    sort(towns.begin(), towns.end(), [](Town a, Town b){return a.y < b.y;});  // aがbより先頭にくるときtrueになるように。
    for(int i=0; i < towns.size() - 1; i++){
        roads.push_back(Road{towns[i].id, towns[i+1].id, towns[i+1].y - towns[i].y});
    }

    // kruskal
    // Kruskal
    auto cmp = [](Road left, Road right){
        // leftが先に出てくるときfalse
        return left.d > right.d;
    };
    priority_queue<Road, vector<Road>, decltype(cmp)> que(cmp);
    for(auto iter: roads){
        que.push(iter);
    }
    UnionFind uf(towns.size());
    ll res = 0;
    while(!que.empty()){
        auto popped = que.top();
        que.pop();
    
        // もしだめならやめる
        if(uf.same(popped.from, popped.to)){
            continue;
        }
    
        // 訪問
        uf.unite(popped.from, popped.to);
    
        // 追加
        res += popped.d;
    }

    cout << res << endl;
    
}

// Generated by 1.1.6 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: 16236