import Data.List
import qualified Data.IntMap.Strict as M
import qualified Data.Set as S
import qualified Data.ByteString.Char8 as B

snub = S.toList . S.fromList
readInt = maybe undefined fst . B.readInt
untilFix f x = let x1 = f x in if x1 == x then x else untilFix f x1

kruskal n = kruskalDP n uf0 0 0
  where
    uf0 = fromDistinctAscList [1..n]

kruskalDP _ _ acc _ [] = acc
kruskalDP n uf acc x (((a,b),d):abd)
  | x == n = acc
  | same uf a b = kruskalDP n uf acc x abd
  | otherwise = kruskalDP n (merge uf a b) (acc + d) (succ x) abd

main = do
  getLine
  xys <- map ((\[x,y]->(x,y)) . map readInt . B.words) . B.lines <$> B.getContents
  let xyis = zip (snub xys) [1..]
  let xysx = sortOn fst xyis
  let xysy = sortOn (snd.fst) xyis
  print (built xysx xysy)

built xysx xysy = kruskal n edge
  where
    n = length xysx
    edge = sortOn snd (edgex ++ edgey)
    edgex = zipWith (\((xi,_),i) ((xj,_),j) -> ((i,j), xj-xi)) xysx (tail xysx)
    edgey = zipWith (\((_,yi),i) ((_,yj),j) -> ((i,j), yj-yi)) xysy (tail xysy)

data UnionFind = UnionFind {tree :: M.IntMap Point, rk :: M.IntMap Rank}
type Point = Int
type Rank = Int

rep :: UnionFind -> Point -> Point
rep uf = (M.!) (tree uf)

rank :: UnionFind -> Point -> Rank
rank uf = (M.!) (rk uf)

merge :: UnionFind -> Point -> Point -> UnionFind
merge uf p1 p2
  | r1 /= r2 && rank uf r1 > rank uf r2 = UnionFind (M.insert p2 r1 (M.insert r2 r1 (tree uf))) (M.insert r1 (r1 + r2) (rk uf)) 
  | r1 /= r2 = UnionFind (M.insert p1 r2 (M.insert r1 r2 (tree uf))) (M.insert r2 (r1 + r2) (rk uf)) 
  | otherwise = uf
  where
    r1 = untilFix (rep uf) p1
    r2 = untilFix (rep uf) p2

same :: UnionFind -> Point -> Point -> Bool
same uf p1 p2 = untilFix (rep uf) p1 == untilFix (rep uf) p2

fromDistinctAscList :: [Point] -> UnionFind
fromDistinctAscList ps = UnionFind tree0 rk0
  where
    tree0 = M.fromDistinctAscList [(i,i) | i <- ps]
    rk0 = M.fromDistinctAscList [(i,1) | i <- ps]
