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

#define rep(i, n) for (int i = 0; i < (n); ++i)

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

using namespace std;

#define MAX_N (int)1e6



typedef long long ll;

const ll INF = 1LL << 60;

typedef pair<ll, ll> P;



int N, E;

vector<ll> x, y;



struct edge { ll u, v, cost; };

bool comp(const edge& e1, const edge& e2) {

    return e1.cost < e2.cost;

}

// edge es[(int)1e6];

vector<edge> es;



class UnionFind {

    int par[MAX_N];  // 親

    int my_rank[MAX_N]; // 木の深さ



public:

    void init(int n) {

        for (int i = 0; i < n; i++) {

            par[i] = i;

            my_rank[i] = 0;

        }

    }



    int find(int x) {

        if (par[x] == x) {

            return x;

        } else {

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

        } } 

    void unite(int x, int y) {

        x = find(x);

        y = find(y);

        if (x == y) {

            return;

        }

        if (my_rank[x] < my_rank[y]) {

            par[x] = y;

        } else {

            par[y] = x;

            if (my_rank[x] == my_rank[y]) my_rank[x]++;

        }

    }



    bool same(int x, int y) {

        return find(x) == find(y);

    }

};



ll kruskal() {

    // N 個

    UnionFind uf;

    uf.init(N);

    // sort(es, es + E, comp);

    sort(es.begin(), es.end(), comp);

    ll res = 0;

    for (ll i = 0; i < E; i++) {

        edge e = es[i];

        if (!uf.same(e.u, e.v)) {

            uf.unite(e.u, e.v);

            res += e.cost;

        }

    }



    return res;

}



int main() {

    cin >> N;

    x.resize(N);

    y.resize(N);

    rep(i, N) {

        cin >> x[i] >> y[i];

    }

    vector<P> x2;

    vector<P> y2;

    rep(i, N) {

        P px(x[i], i);

        x2.push_back(px);

        P py(y[i], i);

        y2.push_back(py);

    }

    sort(x2.begin(), x2.end());

    sort(y2.begin(), y2.end());



    // x の辺

    rep(i, N) {

        if (i - 1 >= 0) {

            edge ex = {x2[i].second, x2[i - 1].second, abs(x2[i].first - x2[i - 1].first)};

            es.push_back(ex);

        }

        if (i + 1 <= N - 1) {

            edge ex2 = {x2[i].second, x2[i + 1].second, abs(x2[i].first - x2[i + 1].first)};

            es.push_back(ex2);

        }

    }



    // y の辺

    rep(i, N) {

        if (i - 1 >= 0) {

            edge ey = {y2[i].second, y2[i - 1].second, abs(y2[i].first - y2[i - 1].first)};

            es.push_back(ey);

        }

        if (i + 1 <= N - 1) {

            edge ey2 = {y2[i].second, y2[i + 1].second, abs(y2[i].first - y2[i + 1].first)};

            es.push_back(ey2);

        }

    }

    E = es.size();

    ll ans = kruskal();

    cout << ans << endl;

}

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

#define rep(i, n) for (int i = 0; i < (n); ++i)

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

using namespace std;

typedef long long ll;

typedef pair<int, int> P;



const ll INF = 1LL << 60;



ll N;

struct edge { ll u, v, cost; };

bool comp(const edge& e1, const edge& e2) {

    return e1.cost < e2.cost;

}

vector<edge> es;



class UnionFind {

public:

	//親の番号を格納する。親だった場合は-(その集合のサイズ)

	vector<int> Parent;

 

	//作るときはParentの値を全て-1にする

	//こうすると全てバラバラになる

	UnionFind(int N) {

		Parent = vector<int>(N, -1);

	}

 

	//Aがどのグループに属しているか調べる

	int root(int A) {

		if (Parent[A] < 0) return A;

		return Parent[A] = root(Parent[A]);

	}

 

	//自分のいるグループの頂点数を調べる

	int size(int A) {

		return -Parent[root(A)];//親をとってきたい]

	}

 

	//AとBをくっ付ける

	bool connect(int A, int B) {

		A = root(A);

		B = root(B);

		if (A == B) {

			return false;

		}

 

		if (size(A) < size(B)) swap(A, B);

 

		Parent[A] += Parent[B];



		Parent[B] = A;

 

		return true;

	}

};



ll kruscal() {

    sort(es.begin(), es.end(), comp);

    UnionFind uf = UnionFind(N);



    ll cost = 0;

    for (int i = 0; i < es.size(); i++) {

        if (uf.root(es[i].u) != uf.root(es[i].v)) {

            uf.connect(es[i].u, es[i].v);

            cost += es[i].cost;

        }

    }



    return cost;

}



int main() {

    cin >> N;

    vector<P> v_x, v_y;

    rep(i, N) {

        ll x, y;

        cin >> x >> y;



        v_x.push_back(P(x, i));

        v_y.push_back(P(y, i));

    }

    sort(v_x.begin(), v_x.end());

    sort(v_y.begin(), v_y.end());



    // x方向

    rep(i, N) {

        if (i - 1 >= 0) {

            // v_x[i].first - 

            edge e = { v_x[i].second, v_x[i - 1].second, v_x[i].first - v_x[i - 1].first};

            es.push_back(e);

        }



        if (i + 1 <= N - 1) {

            edge e = { v_x[i].second, v_x[i + 1].second, v_x[i + 1].first - v_x[i].first};

            es.push_back(e);

        }

    }



    // y方向

    rep(i, N) {

        if (i - 1 >= 0) {

            edge e = { v_y[i].second, v_y[i - 1].second, v_y[i].first - v_y[i - 1].first};

            es.push_back(e);

        }



        if (i + 1 <= N - 1) {

            edge e = { v_y[i].second, v_y[i + 1].second, v_y[i + 1].first - v_y[i].first};

            es.push_back(e);

        }

    }



    ll ans = kruscal();

    cout << ans << endl;

}

// Fastest known solution for this problem: