### BUGGY CODE ###

def main():
    import heapq

    class Prim():
        # 無向グラフであるという前提に注意
        def __init__(self, N):
            self.edge = [[] for i in range(N)]
            self.N = N

        def add(self, u, v, d):
            """
            u = from, v = to, d = cost
            0-indexedであることに注意、graph.add(u-1, v-1)とする必要がある
            """
            self.edge[u].append([d, v])  # コスト、e_toとなっていることに注意
            self.edge[v].append([d, u])

        def delete(self, u, v):
            self.edge[u] = [_ for _ in self.edge[u] if _[0] != v]
            self.edge[v] = [_ for _ in self.edge[v] if _[0] != u]

        def Prim(self):
            """
            return: 最小全域木のコストの和
            """
            used = [True] * self.N  # True:不使用
            edgelist = []
            for e in self.edge[0]:
                heapq.heappush(edgelist, e)
            used[0] = False
            res = 0
            while len(edgelist) != 0:
                minedge = heapq.heappop(edgelist)
                if not used[minedge[1]]:
                    continue
                v = minedge[1]
                used[v] = False
                for e in self.edge[v]:
                    if used[e[1]]:
                        heapq.heappush(edgelist, e)
                res += minedge[0]
            return res

    N = int(input())
    data = [list(map(int, input().split())) for i in range(N)]

    G = Prim(N)

    X = []
    Y = []
    for i in range(G.N):
        X.append((data[i][0], i))
        Y.append((data[i][1], i))

    X = sorted(X)
    Y = sorted(Y)

    # 辞書順で隣り合うものだけ考えれば良い

    for i in range(G.N - 1):
        G.add(X[i][1], X[i + 1][1], X[i + 1][0] - X[i][0])
        G.add(Y[i][1], Y[i + 1][1], Y[i + 1][0] - Y[i][0])

    print(G.Prim())

if __name__ == '__main__':
    main()

### FIXED CODE ###

class Kruskal():
    def __init__(self,n):
        self.e = []
        self.par = [i for i in range(n+1)]  #親のノード番号
        self.rank = [0]*(n+1)
    def add(self,u,v,d):  #クラスカル法で考えるのは無向グラフ
        self.e.append([u,v,d])
    def find(self,x):  #xの根のノード番号
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]
    def same_check(self,x,y):  #x,yが同じグループか否か
        return self.find(x) == self.find(y)
    def unite(self,x,y):  #x,yの属するグループの併合
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        elif self.rank[x] > self.rank[y]:
            self.par[y] = x
        else:
            self.par[y] = x
            self.rank[x] += 1
    def Kruskal(self):
        edges = sorted(self.e,key=lambda x: x[2])  #距離でソート
        a = 0
        for e in edges:
            if not self.same_check(e[0],e[1]):
                self.unite(e[0],e[1])
                a += e[2]
        return a

N = int(input())
A = [list(map(int,input().split())) for i in range(N)]

G = Kruskal(N)

X = []
Y = []
for i in range(N):
    X.append((A[i][0], i))
    Y.append((A[i][1], i))

X = sorted(X)
Y = sorted(Y)

# 辞書順で隣り合うものだけ考えれば良い

for i in range(N - 1):
    G.add(X[i][1], X[i + 1][1], X[i + 1][0] - X[i][0])
    G.add(Y[i][1], Y[i + 1][1], Y[i + 1][0] - Y[i][0])

print(G.Kruskal())