class UnionFindTree {
	int whole_size_m;
	int* parent_p_m;	// 根である場合は自分自身の添え字を入れる
	int* rank_p_m;
	int* size_p_m;		// 集合のサイズ(根に大きさを書く)

	bool Is_valid_index(int index);
	int Root_of_rec(int index);
public:
	UnionFindTree(int whole_size);
	~UnionFindTree();

	int Root_of(int index);
	void Unite(int index_l, int index_r);
	bool Are_together(int index_l, int index_r);
	int Size_of_set(int index);
};



bool UnionFindTree::Is_valid_index(int index) {
	return index >= 0 && index < whole_size_m;
}

int UnionFindTree::Root_of_rec(int index) {
	if (parent_p_m[index] == index) {
		return index;
	} else {
		return parent_p_m[index] = Root_of_rec(parent_p_m[index]);
	}
}

UnionFindTree::UnionFindTree(int whole_size)
	: whole_size_m(whole_size) {
	parent_p_m = new int[whole_size_m];
	rank_p_m = new int[whole_size_m];
	size_p_m = new int[whole_size_m];
	for (int i = 0; i < whole_size_m; ++i) {
		parent_p_m[i] = i;
		rank_p_m[i] = 1;
		size_p_m[i] = 1;
	}
}

UnionFindTree::~UnionFindTree() {
	delete[] parent_p_m;
	delete[] rank_p_m;
	delete[] size_p_m;
}

int UnionFindTree::Root_of(int index) {
	if (Is_valid_index(index) == false) return -1;
	return Root_of_rec(index);
}

void UnionFindTree::Unite(int index_l, int index_r) {
	if (Is_valid_index(index_l) == false || Is_valid_index(index_r) == false) return;
	int root_l = Root_of(index_l);
	int root_r = Root_of(index_r);
	if (root_l == root_r) return;

	if (rank_p_m[root_l] > rank_p_m[root_r]) {
		parent_p_m[root_r] = root_l;
		size_p_m[root_r] += root_l;
	} else {
		parent_p_m[root_l] = root_r;
		size_p_m[root_l] += root_r;
		if (rank_p_m[root_l] == rank_p_m[root_r]) ++rank_p_m[root_r];
	}
}

bool UnionFindTree::Are_together(int index_l, int index_r) {
	return Root_of(index_l) == Root_of(index_r);
}

int UnionFindTree::Size_of_set(int index) {
	return size_p_m[index];
}


#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <vector>
#include <utility>
#include <algorithm>
#include <functional>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <iomanip>
#include <sstream>

using namespace std;
#define REP(i,a,n) for(int i = a; i < (int)(n); ++i)
#define REPM(i,n,a) for(int i = n - 1; i >= a; --i)
#define EPS 0.0001
#define INF 0x3FFFFFFF
#define INFLL 0x3FFFFFFF3FFFFFFF
#define INFD 1.0e+308
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<LL, LL> PP;

#if 1
#include <unordered_set>
#include <unordered_map>
template<typename T>
using PriorityQ = priority_queue<T, vector<T>, greater<T> >;
#endif

int N;
int A[100001], B[100001];
PP PA[100001], PB[100001];
PriorityQ<pair<int, PP> > Q;
int main() {
	cin >> N;
	REP(i, 0, N) {
		cin >> A[i] >> B[i];
		PA[i] = PP(A[i], i);
		PB[i] = PP(B[i], i);
	}
	sort(PA, PA + N);
	sort(PB, PB + N);
	REP(i, 0, N - 1) {
		Q.push(make_pair(PA[i + 1].first - PA[i].first, PP(PA[i + 1].second, PA[i].second)));
		Q.push(make_pair(PB[i + 1].first - PB[i].first, PP(PB[i + 1].second, PB[i].second)));
	}

	UnionFindTree U(N);
	LL sum = 0;
	while (!Q.empty()) {
		auto cp = Q.top(); Q.pop();
		int c = cp.first;
		PP p = cp.second;
		int l = p.first;
		int r = p.second;
		if (!U.Are_together(l, r)) {
			U.Unite(l, r);
			sum += c;
		}
	}
	cout << sum << endl;
	return 0;
}


Measured memory_bytes: 12400