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

class iunionfind {
   public:
    // 連続するuintにのみ使える。vectorであるため定数時間がとれる。
    // 基本的にこれだけあればよい。任意の構造体を扱うときは、IDと構造体のマッピングをとるvectorを別途用意する。
    // libs-test.cppの iuf_map あたりを検索。
    vector<ll> partition;
    vector<ll> rank;

    iunionfind(ll n) {
        partition.resize(n);
        rank.resize(n);
        for (ll x = 0; x < n; x++) {
            partition[x] = x;
            rank[x] = 0;
        }
    }
    ll find(ll x) {
        if (partition[x] == x) {
            return x;
        } else {
            partition[x] = find(partition[x]);
            return partition[x];
        }
    }
    void unite(ll x, ll y) {
        x = find(x);
        y = find(y);
        if (x == y) {
            return;
        }
        if (rank[x] < rank[y]) {
            partition[x] = y;
        } else {
            partition[y] = x;
            if (rank[x] == rank[y]) {
                rank[x]++;
            }
        }
    }
    bool same(ll x, ll y) {
        return find(x) == find(y);
    }
};

void print() { cout << endl; }
template <typename Head, typename... Tail>
void print(Head head, Tail... tail) {
    int size = sizeof...(Tail);
    cout << head;
    if (size > 0) {
        cout << " ";
    }
    print(tail...);
}

void print0() {}
template <typename Head, typename... Tail>
void print0(Head head, Tail... tail) {
    cout << head;
    print0(tail...);
}

struct xstruct {
    int64_t val;
};
bool operator<=(const xstruct& lhs, const xstruct& rhs) {
    return lhs.val <= rhs.val;
}
bool operator<(const xstruct& lhs, const xstruct& rhs) {
    return lhs.val < rhs.val;
}
bool operator>=(const xstruct& lhs, const xstruct& rhs) {
    return lhs.val >= rhs.val;
}
bool operator>(const xstruct& lhs, const xstruct& rhs) {
    return lhs.val > rhs.val;
}
bool operator==(const xstruct& lhs, const xstruct& rhs) {
    return lhs.val == rhs.val;
}
bool operator!=(const xstruct& lhs, const xstruct& rhs) {
    return lhs.val != rhs.val;
}
ostream& operator<<(ostream& os, xstruct const& xst) {
    return os << xst.val;
}

struct city {
    ll x;
    ll y;
    ll id;
};
struct edg {
    ll u;
    ll v;
    ll cost;
};
int main() {
    ll N;
    cin >> N;
    vector<city> cities(N);
    for (ll i = 0; i < N; i++) {
        cin >> cities[i].x;
        cin >> cities[i].y;
        cities[i].id = i;
    }
    vector<city> cities_x(N);
    vector<city> cities_y(N);
    copy(cities.begin(), cities.end(), cities_x.begin());
    copy(cities.begin(), cities.end(), cities_y.begin());

    sort(cities_x.begin(), cities_x.end(), [](city a, city b) { return a.x < b.x; });
    sort(cities_y.begin(), cities_y.end(), [](city a, city b) { return a.y < b.y; });

    iunionfind* uf = new iunionfind(N);

    map<ll, vector<city>> same_x;
    for (ll i = 0; i < N; i++) {
        if (same_x[cities[i].x].size() > 0) {
            uf->unite(cities[i].id, same_x[cities[i].x][0].id);
        }
        same_x[cities[i].x].push_back(cities[i]);
    }
    map<ll, vector<city>> same_y;
    for (ll i = 0; i < N; i++) {
        if (same_y[cities[i].y].size() > 0) {
            uf->unite(cities[i].id, same_y[cities[i].y][0].id);
        }
        same_y[cities[i].y].push_back(cities[i]);
    }

    vector<edg> dists;
    for (ll i = 1; i < N; i++) {
        city x1 = cities_x[i - 1];
        city x2 = cities_x[i];
        if (x1.x != x2.x) {
            dists.push_back({u : x1.id, v : x2.id, cost : x2.x - x1.x});
        }
    }
    for (ll i = 1; i < N; i++) {
        city y1 = cities_y[i - 1];
        city y2 = cities_y[i];
        if (y1.y != y2.y) {
            dists.push_back({u : y1.id, v : y2.id, cost : y2.y - y1.y});
        }
    }
    sort(dists.begin(), dists.end(), [](edg a, edg b) { return a.cost < b.cost; });

    ll total_cost = 0;
    for (auto edg : dists) {
        if (uf->same(edg.u, edg.v)) {
            continue;
        }
        total_cost += edg.cost;
        uf->unite(edg.u, edg.v);
    }
    cout << total_cost << endl;
}


Measured memory_bytes: 40036