# Original solution
def solve_abc065d():
    from collections import namedtuple
    from heapq import heappush, heappop
    from operator import attrgetter
    from itertools import tee
    import sys

    input = sys.stdin.readline

    City = namedtuple('City', 'x y idx')
    Edge = namedtuple('Edge', 'cost to')
    N = int(eval(input()))

    cities = []
    for city_idx in range(N):
        x, y = list(map(int, input().split()))
        c_ = City(x, y, city_idx)
        cities.append(c_)
    cities_x_asc = sorted(cities, key=attrgetter('x'))
    cities_y_asc = sorted(cities, key=attrgetter('y'))

    graph = tuple(set() for _ in range(N))

    it_x = iter(cities_x_asc)
    gx1, gx2 = tee(it_x, 2)
    next(gx2)
    for c1, c2 in zip(gx1, gx2):
        d_ = c2.x - c1.x
        graph[c1.idx].add(Edge(d_, c2.idx))
        graph[c2.idx].add(Edge(d_, c1.idx))
    # x方向にN-1本の辺を張る

    it_y = iter(cities_y_asc)
    gy1, gy2 = tee(it_y, 2)
    next(gy2)
    for c1, c2 in zip(gy1, gy2):
        d_ = c2.y - c1.y
        graph[c1.idx].add(Edge(d_, c2.idx))
        graph[c2.idx].add(Edge(d_, c1.idx))
    # y方向にN-1本の辺を張る

    # <Prim’s Algorithm>
    # https://tjkendev.github.io/procon-library/python/graph/min_st_prim.html
    # プリム法では、グラフGの1頂点からなる木から始め、
    # 木に含まれる頂点と木に含まれない頂点を繋ぐ辺のうち、
    # 一番コストが小さい辺が繋ぐ(木に含まれない方の)頂点を木に追加する、
    # ことを繰り返すことで最小全域木を求める。
    # G[v] = [w, ...]
    #     グラフG上で頂点vが隣接する辺集合

    start = 0  # 起点とする頂点

    used = [-1] * N
    used[start] = 1

    h = []
    for e_ in graph[start]:
        heappush(h, e_)

    mi = 0
    while h:
        e_ = heappop(h)
        if ~used[e_.to]: continue

        used[e_.to] = 1
        mi += e_.cost

        for ne_ in graph[e_.to]:
            if ~used[ne_.to]: continue
            heappush(h, ne_)

    # miが最小全域木の解

    print(mi)
    return


if __name__ == '__main__':
    solve_abc065d()


# Optimized solution
class UnionFind:
    def __init__(self, n):
        self.v = [-1] * n

    def find(self, x):
        if self.v[x] < 0:
            return x
        else:
            self.v[x] = self.find(self.v[x])
            return self.v[x]

    def unite(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return False
        else:
            if self.v[x] > self.v[y]:
                x, y = y, x
            self.v[x] += self.v[y]
            self.v[y] = x
            return True

    def same(self, x, y):
        return self.find(x) == self.find(y)

    def size(self, x):
        return -self.v[self.find(x)]


def main():
    from operator import itemgetter
    import sys
    input = sys.stdin.readline

    N = int(eval(input()))

    ps = []
    xs = set()
    ys = set()
    for i in range(N):
        x, y = list(map(int, input().split()))
        ps.append((x, y))
        xs.add(x)
        ys.add(y)

    xs = tuple(sorted(xs))
    ys = tuple(sorted(ys))

    dx = {e: i for i, e in enumerate(xs)}
    dy = {e: i for i, e in enumerate(ys)}

    uf = UnionFind(N * 2)
    for x, y in ps:
        uf.unite(dx[x], dy[y] + N)

    def make_edges(xs, d, *, add_val=0):
        xes = []
        for x0, x1 in zip(xs, xs[1:]):
            xes.append((abs(x1 - x0), d[x0] + add_val, d[x1] + add_val))
        return xes

    xes = make_edges(xs, dx)
    yes = make_edges(ys, dy, add_val=N)

    # Kruskal
    # 最小コストの辺から順に使用する
    # 同じ連結成分に属していればつながない(閉路を作らない)
    # コスト昇順ソート済みの辺リストを使用する
    ret = 0
    joined = set()
    for cost, a, b in sorted(xes + yes, key=itemgetter(0)):
        if (a in joined) or (b in joined): continue
        if uf.same(a, b): continue
        uf.unite(a, b)
        ret += cost
    print(ret)


if __name__ == '__main__':
    main()