# Original solution
N = int(eval(input()))
Points = [[i] + list(map(int, input().split())) for i in range(N)]

# x, yそれぞれについてソートして、一番近いところを見れば十分
Edges = []
Points.sort(key=lambda p: p[1])
for i in range(N - 1):
    n1, x1, y1 = Points[i]
    n2, x2, y2 = Points[i + 1]
    Edges.append([n1, n2, x2 - x1])

Points.sort(key=lambda p: p[2])
for i in range(N - 1):
    n1, x1, y1 = Points[i]
    n2, x2, y2 = Points[i + 1]
    Edges.append([n1, n2, y2 - y1])


class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n+1)]
        self.rank = [0] * (n+1)
        self.size = [1] * (n+1)

    # 検索
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # 併合
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

    # 同じ集合に属するか判定
    def same_check(self, x, y):
        return self.find(x) == self.find(y)


uf = UnionFind(N)
Edges.sort(key=lambda e: e[2], reverse=True)
MST_cost = 0
i = 0

# 辺のコストが小さいものから、閉路を作らないように、木を構成する
# 最小全域木の必要辺は(頂点数-1)本
while i != N - 1:
    x, y, cost = Edges.pop()
    # 同じ辺集合に属していないならば（閉路が生まれないならば）
    if not uf.same_check(x, y):
        MST_cost += cost
        uf.union(x, y)
        i += 1

print(MST_cost)


# Optimized solution
N = int(eval(input()))
Towns = [[i] + list(map(int, input().split())) for i in range(N)]


class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n)]
        self.rank = [0] * n
        self.size = [1] * n

    # 検索
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # 併合
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.rank[x] < self.rank[y]:
            self.par[x] = y
            self.size[y] += self.size[x]
            self.size[x] = 0
        else:
            self.par[y] = x
            self.size[x] += self.size[y]
            self.size[y] = 0
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

    # 同じ集合に属するか判定
    def same(self, x, y):
        return self.find(x) == self.find(y)

    # すべての頂点に対して親を検索する
    def all_find(self):
        for n in range(len(self.par)):
            self.find(n)


E = []
# xについてソート
Towns.sort(key=lambda t: t[1])
for i in range(1, N):
    t_prev, t_now = Towns[i - 1], Towns[i]
    E.append([t_prev[0], t_now[0], t_now[1] - t_prev[1]])
# yについてソート
Towns.sort(key=lambda t: t[2])
for i in range(1, N):
    t_prev, t_now = Towns[i - 1], Towns[i]
    E.append([t_prev[0], t_now[0], t_now[2] - t_prev[2]])

E.sort(reverse=True, key=lambda e: e[2])
UF = UnionFind(N)

MST_cost = 0
i = 0
# 辺のコストが小さいものから、閉路を作らないように、木を構成する
# 最小全域木の必要辺は(頂点数-1)本
while i != N - 1:
    x, y, cost = E.pop()
    # 同じ辺集合に属していないならば（閉路が生まれないならば）
    if not UF.same(x, y):
        MST_cost += cost
        UF.union(x, y)
        i += 1

print(MST_cost)