#[allow(unused_imports)]
use std::io::{stdin, Read, StdinLock};
#[allow(unused_imports)]
use std::cmp::{max, min, Ordering};
#[allow(unused_imports)]
use std::str::FromStr;
#[allow(unused_imports)]
use std::collections::{HashSet, HashMap, BinaryHeap};
#[allow(unused_imports)]
use std::vec::Vec;

#[allow(dead_code)]
const INF: i32 = 1000_000_000;
#[allow(dead_code)]
const INFLL: i64 = 1000_000_000_000_000_000;
#[allow(dead_code)]
const EPS: f64 = 1.0e-10;
#[allow(dead_code)]
const MOD: i32 = 1000_000_007;

struct Scanner<'a> {
    cin: StdinLock<'a>,
}

impl<'a> Scanner<'a> {
    #[allow(dead_code)]
    fn new(cin: StdinLock<'a>) -> Scanner<'a> {
        Scanner {cin: cin}
    }

    #[allow(dead_code)]
    fn read1<T: FromStr>(&mut self) -> Option<T> {
        let token = self.cin.by_ref().bytes().map(|c| c.unwrap() as char)
                    .skip_while(|c| c.is_whitespace())
                    .take_while(|c| !c.is_whitespace())
                    .collect::<String>();
        token.parse::<T>().ok()
    }

    #[allow(dead_code)]
    fn read<T: FromStr>(&mut self) -> T {
        self.read1().unwrap()
    }
}

struct UnionFind {
    par: Vec<i32>,
}

impl UnionFind {
    #[allow(dead_code)]
    fn new(n: usize) -> UnionFind {
        UnionFind {
            par: vec![-1; n],
        }
    }

    #[allow(dead_code)]
    fn root(&mut self, x: usize) -> usize {
        if self.par[x] < 0 {
            x
        } else {
            let p = self.par[x] as usize;
            self.par[x] = self.root(p) as i32;
            self.par[x] as usize
        }
    }

    #[allow(dead_code)]
    fn unite(&mut self, x: usize, y: usize) -> bool {
        let x = self.root(x);
        let y = self.root(y);
        if x == y {
            false
        } else {
            if self.size(x) < self.size(y) {
                self.par[y] += self.par[x];
                self.par[x] = y as i32; // usize <-> i32
            } else {
                self.par[x] += self.par[y];
                self.par[y] = x as i32;
            }
            true
        }
    }

    #[allow(dead_code)]
    fn same(&mut self, x: usize, y: usize) -> bool {
        self.root(x) == self.root(y)
    }

    #[allow(dead_code)]
    fn size(&mut self, x: usize) -> usize {
        let r = self.root(x);
        (-self.par[r]) as usize
    }
}

#[derive(Eq, PartialEq, PartialOrd)]
struct Edge {
    from: usize,
    to: usize,
    weight: i32,
}

impl Edge {
    fn new(from: usize, to: usize, weight: i32) -> Edge {
        Edge { from: from, to: to, weight: weight }
    }
}

impl Ord for Edge {
    fn cmp(&self, other: &Edge) -> Ordering {
        if self.weight > other.weight {
            Ordering::Greater
        } else if self.weight < other.weight {
            Ordering::Less
        } else {
            Ordering::Equal
        }
    }
}

/// Kruskal algorithm (Minimum Spanning Tree)
/// param V: number of nodes
/// param edges: edges of graph
#[allow(dead_code)]
fn kruskal(vsize: usize, edges: &mut Vec<Edge>) -> Vec<Edge> {
    let mut res: Vec<Edge> = Vec::new();
    let mut uf: UnionFind = UnionFind::new(vsize);
    edges.sort_by(|e1, e2| e1.cmp(e2));
    for e in edges {
        //println!("kruskal: {} {} {}", e.from, e.to, e.weight);
        if !uf.same(e.from, e.to) {
            uf.unite(e.from, e.to);
            res.push(Edge::new(e.from, e.to, e.weight));
        }
    }
    res
}

fn main() {
    let cin = stdin();
    let cin = cin.lock();
    let mut sc = Scanner::new(cin);
    let n: usize = sc.read();
    let mut x: Vec<(i32, usize)> = vec![(0, 0); n];
    let mut y: Vec<(i32, usize)> = vec![(0, 0); n];
    for i in 0..n {
        let xx: i32 = sc.read();
        let yy: i32 = sc.read();
        x[i] = (xx, i);
        y[i] = (yy, i);
    }
    x.sort(); y.sort();
    let mut roads: Vec<Edge> = Vec::new();
    for i in 0..n-1 {
        roads.push(Edge::new(x[i].1, x[i + 1].1, x[i + 1].0 - x[i].0));
        roads.push(Edge::new(y[i].1, y[i + 1].1, y[i + 1].0 - y[i].0));
    }
    let mst = kruskal(n, &mut roads);
    let mut ans = 0;
    for e in &mst {
        ans += e.weight;
    }
    println!("{}", ans);
}

Measured memory_bytes: 19808