// Slower C++ solution:
#include <bits/stdc++.h>

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>



using namespace std;

using namespace __gnu_pbds;



#define fi first

#define se second

#define mp make_pair

#define pb push_back

#define fbo find_by_order

#define ook order_of_key



typedef long long ll;

typedef pair<ll,ll> ii;

typedef vector<int> vi;

typedef long double ld;

typedef tree<int, null_type, less<int>, rb_tree_tag, tree_order_statistics_node_update> pbds;

bool marked[100001];

vector<ii> v[100001];  //adjList



ll prim(int k){

    priority_queue<ii, vector<ii>, greater<ii> > q;

    ll u;

    long long minimumCost = 0;

    ii p;

    q.push(mp(0, k));

    while(!q.empty())

    {

        p = q.top();

        q.pop();

        k = p.second;

        if(marked[k] == true)

            continue;

        minimumCost += p.fi;

        marked[k] = true;

        for(ll i = 0;i < v[k].size();++i)

        {

            u = v[k][i].se;

            if(marked[u] == false)

                q.push(v[k][i]);

        }

    }

    return minimumCost;

}



int main() {

    std::ios::sync_with_stdio(false);

    cin.tie(NULL);

    long long n;

    cin>>n;

    vector<ii> x;

    vector<ii> y;

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

        ll a,b;

        cin>>a>>b;

        x.pb(mp(a,i));

        y.pb(mp(b,i));

    }

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

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

    for(ll i=0;i<n-1;i++){

        v[x[i].se].pb(mp(x[i+1].fi-x[i].fi,x[i+1].se));

        v[x[i+1].se].pb(mp(x[i+1].fi-x[i].fi,x[i].se));

        v[y[i].se].pb(mp(y[i+1].fi-y[i].fi,y[i+1].se));

        v[y[i+1].se].pb(mp(y[i+1].fi-y[i].fi,y[i].se));

    }

    ll ans=prim(0);

    cout<<ans;

    return 0;

}


// Faster C++ solution for the same problem (measured speedup: 3.534038266027416x):
#include<bits/stdc++.h>

const int N = 1e5+5;

using namespace std;

int n;

struct Node{

	int x;

	int y;

	int id;

	void show(){

		cout << this->x <<" "<<this->y<<" "<<this->id<<"\n";

	}

}a[N];

bool cmp1(const Node &a, const Node &b){

	if(a.x < b.x) return 1;

	if(a.x == b.x) return a.y < b.y;

	return 0;

}

bool cmp2(const Node &a, const Node &b){

	if(a.y < b.y) return 1;

	if(a.x == b.x) return a.x < b.x;

	return 0;

}

struct cmpp{

	bool operator()(const Node &a, const Node &b){

		if(a.x > b.x) return 1;

		if(a.x == b.x) return a.y > b.y;

		return 0;

	}

};

priority_queue<tuple<int,int,int>, vector<tuple<int,int,int>>, greater<tuple<int,int,int>> > pq;



int counting;

vector<int> par, children;



void Start(int n){

	counting = n;

 	par.assign(n+1,-1);

	children.assign(n+1,1);

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

		par[i] = i;

	}

	return;

}



int Find(int i){

	if(par[i] != i) par[i] = Find(par[i]);

	return par[i];

}



bool Union(int i, int j){

	i = Find(i);

	j = Find(j);

	if(i != j){

		if(children[i] < children[j]){

			par[i] = j;

			children[j] += children[i];	

		}else{

			par[j] = i;

			children[i] += children[j];

		}

		counting--;

	}else return 0;

	return 1;

}



signed main(){

	cin >> n;

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

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

		a[i].id = i; 

		

	}

	

	sort(a+1, a+1+n, cmp1);

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

		if(i!=1) pq.push(make_tuple(abs(a[i].x - a[i-1].x), a[i].id, a[i-1].id));

	}



	sort(a+1, a+1+n, cmp2);

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

		if(i!=1) pq.push(make_tuple(abs(a[i].y - a[i-1].y), a[i].id, a[i-1].id));

	}

	int ans = 0, dist, u, v;

	Start(n);

	while(pq.size()){

		if(counting == 1) break;

		tie(dist, u, v) = pq.top();

		pq.pop();

		if(Union(u, v)){ 

			ans += dist;

		}

	}

	cout << ans <<"\n";

}