#最小全域木　プリム法

from heapq import *



n=int(eval(input()))

xy=[]

for i in range(n):

    x,y=list(map(int,input().split()))

    xy.append([x,y,i])



#今回の問題では,高々xが隣のやつとyが隣のやつを全部考えればよし

#（詳しくは解説pdfより）

x_sort = sorted(xy, key=lambda x: x[0])

y_sort = sorted(xy, key=lambda x: x[1])



cost = [[]for _ in range(n+3)] #cost[i]にはi番目から(j,k)でj番目にコストkかかる

for i in range(n-1):

    cost[x_sort[i][2]].append((x_sort[i+1][2],x_sort[i+1][0]-x_sort[i][0]))

    cost[x_sort[i+1][2]].append((x_sort[i][2],x_sort[i+1][0]-x_sort[i][0]))



    cost[y_sort[i][2]].append((y_sort[i+1][2],y_sort[i+1][1]-y_sort[i][1]))

    cost[y_sort[i+1][2]].append((y_sort[i][2],y_sort[i+1][1]-y_sort[i][1]))





used=[False]*(n+3)#頂点が最小全域木にもうすでに含まれているかどうか

q=[]

#すでに最小全域木が決まっている部分からまだ決まっていない部分への辺をヒープで管理

#(a,b)でaには向かう先の頂点,bにはかかるコスト

#ヒープで小さいコストからとりだせる



#初期条件

#はじめはどこでもよいので０番目スタートとする(0番目だけではコストは0)

used[0] = True

ans = 0



#0番目からの辺をすべてヒープに挿入

for to,cst in cost[0]:

    heappush(q,(cst,to))



#すでに決まっている最小全域木部分からまだ決まっていない部分を貪欲的にひたすら追加する

#はじめは初期条件の通り

while(len(q)!=0):

    c,ver=heappop(q)

#すでに決まっている部分から一番小さいコストの辺を取り出す

    if used[ver] == True:

        continue

#もしすでに最小全域木部分であればやり直し

#そうでなければコストの分を加算し、新しい頂点を最小全域木部分に追加する

    ans += c

    used[ver] = True

#新しく追加した頂点からの辺をヒープに追加する

    for to,cst in cost[ver]:

        heappush(q,(cst,to))

#これを貪欲的に繰り返すことでうまくいくらしい

print(ans)

#最小全域木　クラスカル法

class UnionFind():

    # 作りたい要素数nで初期化

    # 使用するインスタンス変数の初期化

    def __init__(self, n):

        self.n = n

        # root[x]<0ならそのノードが根かつその値が木の要素数

        # rootノードでその木の要素数を記録する

        self.root = [-1]*(n+1)

        # 木をくっつける時にアンバランスにならないように調整する

        self.rnk = [0]*(n+1)



    # ノードxのrootノードを見つける

    def find_root(self, x):

        if(self.root[x] < 0):

            return x

        else:

            # ここで代入しておくことで、後の繰り返しを避ける

            #経路圧縮

            self.root[x] = self.find_root(self.root[x])

            return self.root[x]

    # 木の併合、入力は併合したい各ノード

    def unite(self, x, y):

        # 入力ノードのrootノードを見つける

        x = self.find_root(x)

        y = self.find_root(y)

        # すでに同じ木に属していた場合

        if(x == y):

            return

        # 違う木に属していた場合rnkを見てくっつける方を決める

        elif(self.rnk[x] > self.rnk[y]):

            self.root[x] += self.root[y]

            self.root[y] = x



        else:

            self.root[y] += self.root[x]

            self.root[x] = y

            # rnkが同じ（深さに差がない場合）は1増やす

            if(self.rnk[x] == self.rnk[y]):

                self.rnk[y] += 1

    # xとyが同じグループに属するか判断

    def isSame(self, x, y):

        return self.find_root(x) == self.find_root(y)



    # ノードxが属する木のサイズを返す

    def size(self, x):

        return -self.root[self.find_root(x)]



n=int(eval(input()))

xy=[]

for i in range(n):

    x,y=list(map(int,input().split()))

    xy.append([x,y,i])



#今回の問題では,高々xが隣のやつとyが隣のやつを全部考えればよし

#（詳しくは解説pdfより）

x_sort = sorted(xy, key=lambda x: x[0])

y_sort = sorted(xy, key=lambda x: x[1])



edge = [] #edge=[i番目と,j番目が,コストk]

for i in range(n-1):

    edge.append([x_sort[i][2],x_sort[i+1][2],x_sort[i+1][0]-x_sort[i][0]])

    edge.append([y_sort[i][2],y_sort[i+1][2],y_sort[i+1][1]-y_sort[i][1]])



#クラスカル法では辺のコストを小さい順にみていき閉路ができなければ追加する。を繰り返す

#閉路ができるかどうかはUnionFindで効率的に判定できる

ans = 0

edge.sort(key=lambda x:x[2])

u=UnionFind(n)

for i,j,cost in edge:

    if u.isSame(i,j) == False:

        u.unite(i,j)

        ans+=cost



print(ans)
# 最小全域木　プリム法
from heapq import *

n = int(eval(input()))
xy = []
for i in range(n):
    x, y = list(map(int, input().split()))
    xy.append([x, y, i])
# 今回の問題では,高々xが隣のやつとyが隣のやつを全部考えればよし
# （詳しくは解説pdfより）
x_sort = sorted(xy, key=lambda x: x[0])
y_sort = sorted(xy, key=lambda x: x[1])
cost = [[] for _ in range(n + 3)]  # cost[i]にはi番目から(j,k)でj番目にコストkかかる
for i in range(n - 1):
    cost[x_sort[i][2]].append((x_sort[i + 1][2], x_sort[i + 1][0] - x_sort[i][0]))
    cost[x_sort[i + 1][2]].append((x_sort[i][2], x_sort[i + 1][0] - x_sort[i][0]))
    cost[y_sort[i][2]].append((y_sort[i + 1][2], y_sort[i + 1][1] - y_sort[i][1]))
    cost[y_sort[i + 1][2]].append((y_sort[i][2], y_sort[i + 1][1] - y_sort[i][1]))
used = [False] * (n + 3)  # 頂点が最小全域木にもうすでに含まれているかどうか
q = []
# すでに最小全域木が決まっている部分からまだ決まっていない部分への辺をヒープで管理
# (a,b)でaには向かう先の頂点,bにはかかるコスト
# ヒープで小さいコストからとりだせる
# 初期条件
# はじめはどこでもよいので０番目スタートとする(0番目だけではコストは0)
used[0] = True
ans = 0
# 0番目からの辺をすべてヒープに挿入
for to, cst in cost[0]:
    heappush(q, (cst, to))
# すでに決まっている最小全域木部分からまだ決まっていない部分を貪欲的にひたすら追加する
# はじめは初期条件の通り
while len(q) != 0:
    c, ver = heappop(q)
    # すでに決まっている部分から一番小さいコストの辺を取り出す
    if used[ver] == True:
        continue
    # もしすでに最小全域木部分であればやり直し
    # そうでなければコストの分を加算し、新しい頂点を最小全域木部分に追加する
    ans += c
    used[ver] = True
    # 新しく追加した頂点からの辺をヒープに追加する
    for to, cst in cost[ver]:
        heappush(q, (cst, to))
# これを貪欲的に繰り返すことでうまくいくらしい
print(ans)

# 最小全域木　クラスカル法
class UnionFind:
    # 作りたい要素数nで初期化
    # 使用するインスタンス変数の初期化
    def __init__(self, n):
        self.n = n
        # root[x]<0ならそのノードが根かつその値が木の要素数
        # rootノードでその木の要素数を記録する
        self.root = [-1] * (n + 1)
        # 木をくっつける時にアンバランスにならないように調整する
        self.rnk = [0] * (n + 1)

    # ノードxのrootノードを見つける
    def find_root(self, x):
        if self.root[x] < 0:
            return x
        else:
            # ここで代入しておくことで、後の繰り返しを避ける
            # 経路圧縮
            self.root[x] = self.find_root(self.root[x])
            return self.root[x]

    # 木の併合、入力は併合したい各ノード
    def unite(self, x, y):
        # 入力ノードのrootノードを見つける
        x = self.find_root(x)
        y = self.find_root(y)
        # すでに同じ木に属していた場合
        if x == y:
            return
        # 違う木に属していた場合rnkを見てくっつける方を決める
        elif self.rnk[x] > self.rnk[y]:
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            # rnkが同じ（深さに差がない場合）は1増やす
            if self.rnk[x] == self.rnk[y]:
                self.rnk[y] += 1

    # xとyが同じグループに属するか判断
    def isSame(self, x, y):
        return self.find_root(x) == self.find_root(y)

    # ノードxが属する木のサイズを返す
    def size(self, x):
        return -self.root[self.find_root(x)]


n = int(eval(input()))
xy = []
for i in range(n):
    x, y = list(map(int, input().split()))
    xy.append([x, y, i])
# 今回の問題では,高々xが隣のやつとyが隣のやつを全部考えればよし
# （詳しくは解説pdfより）
x_sort = sorted(xy, key=lambda x: x[0])
y_sort = sorted(xy, key=lambda x: x[1])
edge = []  # edge=[i番目と,j番目が,コストk]
for i in range(n - 1):
    edge.append([x_sort[i][2], x_sort[i + 1][2], x_sort[i + 1][0] - x_sort[i][0]])
    edge.append([y_sort[i][2], y_sort[i + 1][2], y_sort[i + 1][1] - y_sort[i][1]])
# クラスカル法では辺のコストを小さい順にみていき閉路ができなければ追加する。を繰り返す
# 閉路ができるかどうかはUnionFindで効率的に判定できる
ans = 0
edge.sort(key=lambda x: x[2])
u = UnionFind(n)
for i, j, cost in edge:
    if u.isSame(i, j) == False:
        u.unite(i, j)
        ans += cost
print(ans)

-# 最小全域木　プリム法 -from heapq import * +# 最小全域木　クラスカル法 +class UnionFind: +    # 作りたい要素数nで初期化 +    # 使用するインスタンス変数の初期化 +    def __init__(self, n): +        self.n = n +        # root[x]<0ならそのノードが根かつその値が木の要素数 +        # rootノードでその木の要素数を記録する +        self.root = [-1] * (n + 1) +        # 木をくっつける時にアンバランスにならないように調整する +        self.rnk = [0] * (n + 1) + +    # ノードxのrootノードを見つける +    def find_root(self, x): +        if self.root[x] < 0: +            return x +        else: +            # ここで代入しておくことで、後の繰り返しを避ける +            # 経路圧縮 +            self.root[x] = self.find_root(self.root[x]) +            return self.root[x] + +    # 木の併合、入力は併合したい各ノード +    def unite(self, x, y): +        # 入力ノードのrootノードを見つける +        x = self.find_root(x) +        y = self.find_root(y) +        # すでに同じ木に属していた場合 +        if x == y: +            return +        # 違う木に属していた場合rnkを見てくっつける方を決める +        elif self.rnk[x] > self.rnk[y]: +            self.root[x] += self.root[y] +            self.root[y] = x +        else: +            self.root[y] += self.root[x] +            self.root[x] = y +            # rnkが同じ（深さに差がない場合）は1増やす +            if self.rnk[x] == self.rnk[y]: +                self.rnk[y] += 1 + +    # xとyが同じグループに属するか判断 +    def isSame(self, x, y): +        return self.find_root(x) == self.find_root(y) + +    # ノードxが属する木のサイズを返す +    def size(self, x): +        return -self.root[self.find_root(x)] + -cost = [[] for _ in range(n + 3)]  # cost[i]にはi番目から(j,k)でj番目にコストkかかる +edge = []  # edge=[i番目と,j番目が,コストk] -    cost[x_sort[i][2]].append((x_sort[i + 1][2], x_sort[i + 1][0] - x_sort[i][0])) -    cost[x_sort[i + 1][2]].append((x_sort[i][2], x_sort[i + 1][0] - x_sort[i][0])) -    cost[y_sort[i][2]].append((y_sort[i + 1][2], y_sort[i + 1][1] - y_sort[i][1])) -    cost[y_sort[i + 1][2]].append((y_sort[i][2], y_sort[i + 1][1] - y_sort[i][1])) -used = [False] * (n + 3)  # 頂点が最小全域木にもうすでに含まれているかどうか -q = [] -# すでに最小全域木が決まっている部分からまだ決まっていない部分への辺をヒープで管理 -# (a,b)でaには向かう先の頂点,bにはかかるコスト -# ヒープで小さいコストからとりだせる -# 初期条件 -# はじめはどこでもよいので０番目スタートとする(0番目だけではコストは0) -used[0] = True +    edge.append([x_sort[i][2], x_sort[i + 1][2], x_sort[i + 1][0] - x_sort[i][0]]) +    edge.append([y_sort[i][2], y_sort[i + 1][2], y_sort[i + 1][1] - y_sort[i][1]]) +# クラスカル法では辺のコストを小さい順にみていき閉路ができなければ追加する。を繰り返す +# 閉路ができるかどうかはUnionFindで効率的に判定できる -# 0番目からの辺をすべてヒープに挿入 -for to, cst in cost[0]: -    heappush(q, (cst, to)) -# すでに決まっている最小全域木部分からまだ決まっていない部分を貪欲的にひたすら追加する -# はじめは初期条件の通り -while len(q) != 0: -    c, ver = heappop(q) -    # すでに決まっている部分から一番小さいコストの辺を取り出す -    if used[ver] == True: -        continue -    # もしすでに最小全域木部分であればやり直し -    # そうでなければコストの分を加算し、新しい頂点を最小全域木部分に追加する -    ans += c -    used[ver] = True -    # 新しく追加した頂点からの辺をヒープに追加する -    for to, cst in cost[ver]: -        heappush(q, (cst, to)) -# これを貪欲的に繰り返すことでうまくいくらしい +edge.sort(key=lambda x: x[2]) +u = UnionFind(n) +for i, j, cost in edge: +    if u.isSame(i, j) == False: +        u.unite(i, j) +        ans += cost