#[allow(unused_macros)]
macro_rules ! read_value {($ next : expr , ($ ($ t : tt ) ,* ) ) => {($ (read_value ! ($ next , $ t ) ) ,* ) } ; ($ next : expr , [$ t : tt ; $ len : expr ] ) => {(0 ..$ len ) . map (| _ | read_value ! ($ next , $ t ) ) . collect ::< Vec < _ >> () } ; ($ next : expr , chars ) => {read_value ! ($ next , String ) . chars () . collect ::< Vec < char >> () } ; ($ next : expr , usize1 ) => {read_value ! ($ next , usize ) - 1 } ; ($ next : expr , $ t : ty ) => {$ next () . parse ::<$ t > () . expect ("Parse error" ) } ; }
#[allow(unused_macros)]
macro_rules ! input_inner {($ next : expr ) => {} ; ($ next : expr , ) => {} ; ($ next : expr , $ var : ident : $ t : tt $ ($ r : tt ) * ) => {let $ var = read_value ! ($ next , $ t ) ; input_inner ! {$ next $ ($ r ) * } } ; }
#[allow(unused_macros)]
macro_rules ! input {(source = $ s : expr , $ ($ r : tt ) * ) => {let mut iter = $ s . split_whitespace () ; let mut next = || {iter . next () . unwrap () } ; input_inner ! {next , $ ($ r ) * } } ; ($ ($ r : tt ) * ) => {let stdin = std :: io :: stdin () ; let mut bytes = std :: io :: Read :: bytes (std :: io :: BufReader :: new (stdin . lock () ) ) ; let mut next = move || -> String {bytes . by_ref () . map (| r | r . unwrap () as char ) . skip_while (| c | c . is_whitespace () ) . take_while (| c |! c . is_whitespace () ) . collect () } ; input_inner ! {next , $ ($ r ) * } } ; }

#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Edge {
    src: usize,
    dst: usize,
    weight: i32,
}

use std::cmp::Ordering;
impl Ord for Edge {
    fn cmp(&self, other: &Self) -> Ordering {
        if self.weight != other.weight {
            self.weight.cmp(&other.weight)
        } else if self.src != other.src {
            self.src.cmp(&other.src)
        } else {
            self.dst.cmp(&other.dst)
        }
    }
}

impl PartialOrd for Edge {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq, Eq)]
struct Graph {
    g: Vec<Vec<Edge>>
}

#[allow(dead_code)]
impl Graph {
    fn new(n: usize) -> Self {
        Graph{ g: vec![vec![]; n] }
    }

    fn order(&self) -> usize {
        self.g.len()
    }

    fn add_edge(&mut self, src: usize, dst: usize, weight: i32) {
        self.g[src].push(Edge{ src, dst, weight });
        self.g[dst].push(Edge{ src: dst, dst: src, weight });
    }
}

use std::ops::Index;
impl Index<usize> for Graph {
    type Output = Vec<Edge>;

    fn index(&self, v: usize) -> &Self::Output {
        &self.g[v]
    }
}

use std::cmp::Reverse;

#[allow(dead_code)]
/// Prim's algorithm, O(E + V log V)
fn minimum_spanning_tree(g: &Graph, root: usize) -> (i32, Vec<Edge>) {
    let n = g.order();
    let mut total = 0;

    let mut visited = vec![false; n];
    let mut mst = vec![];
    let mut que = std::collections::BinaryHeap::new();  // max-heap
    que.push(Reverse(Edge{ src: root, dst: root, weight: 0 }));
    while let Some(Reverse(e)) = que.pop() {
        if visited[e.dst] {
            continue;
        }
        mst.push(e);
        total += e.weight;
        visited[e.dst] = true;
        for f in g[e.dst].iter() {
            if !visited[f.dst] {
                que.push(Reverse(*f));
            }
        }
    }
    (total, mst)
}

fn main() {
    input! {
        n: usize,
        xy: [(i32, i32); n],
    }

    let mut xpath = xy.iter().enumerate().map(|(i, &(x, _))| (x, i)).collect::<Vec<_>>();
    let mut ypath = xy.iter().enumerate().map(|(i, &(_, y))| (y, i)).collect::<Vec<_>>();
    xpath.sort();
    ypath.sort();

    let mut g = Graph::new(n);
    for i in 1..n {
        g.add_edge(xpath[i - 1].1, xpath[i].1, xpath[i].0 - xpath[i - 1].0);
        g.add_edge(ypath[i - 1].1, ypath[i].1, ypath[i].0 - ypath[i - 1].0);
    }
    let (ret, _) = minimum_spanning_tree(&g, 0);

    println!("{}", ret);
}


Measured memory_bytes: 26580