package main

import (
	"bufio"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
	"unsafe"
)

var (
	sc scanner
)

func main() {
	/// read input
	n := sc.nextInt()
	x, y := make([]int, n), make([]int, n)
	for i := 0; i < n; i++ {
		x[i], y[i] = sc.nextInt(), sc.nextInt()
	}

	/// sort by x and y
	xs, ys := make([]xp, n), make([]yp, n)
	for i := 0; i < n; i++ {
		xs[i].node, xs[i].x, xs[i].y = i+1, x[i], y[i]
		ys[i].node, ys[i].x, ys[i].y = i+1, x[i], y[i]
	}
	sort.Sort(xps(xs))
	sort.Sort(yps(ys))

	/// create edge from sorted points
	es := make([]edge, 0)
	for i := 0; i < len(xs)-1; i++ {
		es = append(es, edge{from: xs[i].node, to: xs[i+1].node, cost: abs(xs[i].x - xs[i+1].x)})
	}
	for i := 0; i < len(ys)-1; i++ {
		es = append(es, edge{from: ys[i].node, to: ys[i+1].node, cost: abs(ys[i].y - ys[i+1].y)})
	}

	/// kruskal
	ans := 0
	sort.Sort(edges(es))
	var uf ufTree
	uf.init(n + 1)
	for _, e := range es {
		if !uf.same(e.from, e.to) {
			uf.unite(e.from, e.to)
			ans += e.cost
		}
	}
	fmt.Println(ans)
}

/***************/
type xp struct {
	node, x, y int
}

type xps []xp

func (x xps) Len() int {
	return len(x)
}

func (x xps) Less(i, j int) bool {
	return x[i].x < x[j].x
}

func (x xps) Swap(i, j int) {
	x[i], x[j] = x[j], x[i]
}

/****************/
type yp struct {
	node, x, y int
}

type yps []yp

func (y yps) Len() int {
	return len(y)
}

func (y yps) Less(i, j int) bool {
	return y[i].y < y[j].y
}

func (y yps) Swap(i, j int) {
	y[i], y[j] = y[j], y[i]
}

/****************/
type edge struct {
	cost, from, to int
}

type edges []edge

func (e edges) Len() int {
	return len(e)
}

func (e edges) Less(i, j int) bool {
	return e[i].cost < e[j].cost
}

func (e edges) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}

/****************/
type ufTree struct {
	parent, rank, size []int
}

func (uf *ufTree) init(n int) {
	uf.parent = make([]int, n)
	uf.rank = make([]int, n)
	uf.size = make([]int, n)
	for i, _ := range uf.parent {
		uf.parent[i] = i
	}
	for i, _ := range uf.rank {
		uf.rank[i] = 0
	}
	for i, _ := range uf.size {
		uf.size[i] = 1
	}
}

func (uf *ufTree) find(x int) int {
	if uf.parent[x] == x {
		return x
	}
	uf.parent[x] = uf.find(uf.parent[x])
	return uf.parent[x]
}

func (uf *ufTree) unite(x, y int) {
	px, py := uf.find(x), uf.find(y)

	if px == py {
		return
	}

	s := uf.size[px] + uf.size[py]

	if uf.rank[px] < uf.rank[py] {
		uf.parent[px] = py
		uf.size[py] = s
		return
	}
	uf.parent[py] = px
	if uf.rank[px] == uf.rank[py] {
		uf.rank[px]++
	}
	uf.size[px] = s
}

func (uf *ufTree) same(x, y int) bool {
	if uf.find(x) == uf.find(y) {
		return true
	}
	return false
}

func (uf *ufTree) getSize(x int) int {
	px := uf.find(x)
	return uf.size[px]
}

/* template functions */

func init() {
	sc = scanner{
		buf: make([]string, 0, 1000000),
		cur: 0,
		r:   bufio.NewReader(os.Stdin),
	}
}

type scanner struct {
	buf []string
	cur int
	r   *bufio.Reader
}

func (s *scanner) readln() {
	rbuf := make([]byte, 0, 30)
	for {
		line, prefix, err := s.r.ReadLine()
		if err != nil {
			panic(err)
		}
		rbuf = append(rbuf, line...)
		if prefix == false {
			break
		}
	}
	s.cur = 0
	s.buf = strings.Split(*(*string)(unsafe.Pointer(&rbuf)), " ")
}

func (s *scanner) isFull() bool {
	if s.cur == len(s.buf) {
		return true
	}
	return false
}

func (s *scanner) resetCur() {
	s.cur = 0
}

func (s *scanner) next() string {
	if s.cur == 0 {
		s.readln()
	}
	res := s.buf[s.cur]
	s.cur++
	if s.isFull() {
		s.resetCur()
	}
	return res
}

func (s *scanner) nexts() []string {
	s.readln()
	s.resetCur()
	return s.buf
}

func (s *scanner) nextInt() int {
	if s.cur == 0 {
		s.readln()
	}
	res, _ := strconv.Atoi(s.buf[s.cur])
	s.cur++
	if s.isFull() {
		s.resetCur()
	}
	return res
}

func (s *scanner) nextInts() []int {
	s.readln()
	res := make([]int, len(s.buf))
	for i, _ := range s.buf {
		res[i], _ = strconv.Atoi(s.buf[i])
	}
	s.resetCur()
	return res
}

func (s *scanner) nextFloat() float64 {
	if s.cur == 0 {
		s.readln()
	}
	res, _ := strconv.ParseFloat(s.buf[s.cur], 64)
	s.cur++
	if s.isFull() {
		s.resetCur()
	}
	return res
}

func (s *scanner) nextFloats() []float64 {
	s.readln()
	res := make([]float64, len(s.buf))
	for i, _ := range s.buf {
		res[i], _ = strconv.ParseFloat(s.buf[i], 64)
	}
	s.resetCur()
	return res
}

func max(x, y int) int {
	if x > y {
		return x
	} else {
		return y
	}
}

func min(x, y int) int {
	if x < y {
		return x
	} else {
		return y
	}
}

func abs(x int) int {
	if x < 0 {
		return -x
	}
	return x
}

func var_dump(value ...interface{}) {
	for _, v := range value {
		fmt.Printf("%#v\n", v)
	}
}


Measured memory_bytes: 32256