// Original C++ solution (slower):
#include <bits/stdc++.h>

#ifndef ONLINE_JUDGE

#define debug(x) cout << #x << ": " << x << endl

#else

#define debug(x)

#endif

using namespace std;

typedef long long ll;

const int MAXN=4e5+7;

const int INF=0x3f3f3f3f;

const int MOD=1e9+7;



int fa[MAXN];

struct edge

{

    int u,v,w;

}e[MAXN];

int tot=0;

void addedge(int u,int v,int w)

{

    e[tot].u=u;

    e[tot].v=v;

    e[tot++].w=w;

}

bool cmp(edge a,edge b){return a.w<b.w;}



int Find(int x)

{

    if(fa[x]==-1) return x;

    else return fa[x]=Find(fa[x]);

}

ll kruskal(int n)

{

    memset(fa,-1,sizeof(fa));

    sort(e,e+tot,cmp);

    int cnt=0;

    ll ans=0;

    for(int i=0;i<tot;++i)

    {

        int u=e[i].u,v=e[i].v,w=e[i].w;

        int t1=Find(u),t2=Find(v);

        if(t1!=t2)

        {

            ans+=w;

            fa[t1]=t2;

            cnt++;

        }

        if(cnt==n-1) return ans;

    }

    return -1;

}



struct node

{

    int x,y,id;

}p[MAXN];

bool cmp1(node a,node b)

{

    return a.x<b.x;

}

bool cmp2(node a,node b)

{

    return a.y<b.y;

}



int main()

{

    ios::sync_with_stdio(false);

    cin.tie(0);

    int n;

    cin>>n;

    for(int i=0;i<n;++i)

    {

        cin>>p[i].x>>p[i].y;

        p[i].id=i+1;

    }

    sort(p,p+n,cmp1);

    for(int i=0;i<n;++i)

    {

        if(i==0) addedge(p[i].id,p[i+1].id,abs(p[i+1].x-p[i].x));

        else if(i==n-1) addedge(p[i].id,p[i-1].id,abs(p[i-1].x-p[i].x));

        else

        {

            addedge(p[i].id,p[i+1].id,abs(p[i+1].x-p[i].x));

            addedge(p[i].id,p[i-1].id,abs(p[i-1].x-p[i].x));

        }

    }

    sort(p,p+n,cmp2);

    for(int i=0;i<n;++i)

    {

        if(i==0) addedge(p[i].id,p[i+1].id,abs(p[i+1].y-p[i].y));

        else if(i==n-1) addedge(p[i].id,p[i-1].id,abs(p[i-1].y-p[i].y));

        else

        {

            addedge(p[i].id,p[i+1].id,abs(p[i+1].y-p[i].y));

            addedge(p[i].id,p[i-1].id,abs(p[i-1].y-p[i].y));

        }

    }

    cout<<kruskal(n)<<endl;

    return 0;

}


// Optimized solution to the same problem (faster):
#include <bits/stdc++.h>

typedef long long LL;

#define FOR(i,a,b) for(int i=(a);i<(b);++i)

#define REP(i,n) FOR(i,0,n)



using namespace std;



class UnionFind {

  std::vector<int> p;



public:

  /// @brief

  /// $0$ から $n - 1$ までそれぞれに対して，その要素だけを格納した集合を作る．

  /// @complexity $O(n)$

  UnionFind(int n) : p(n, -1) {}



  /// @brief

  /// $x$ を含む集合の代表元を返す．

  /// @complexity amortized $O(\\alpha(n))$

  int root(int x) { return p[x] < 0 ? x : p[x] = root(p[x]); }



  /// @brief

  /// $x$ と $y$ が同一の集合に属するかどうかを返す．

  /// @complexity amortized $O(\\alpha(n))$

  bool same(int x, int y) { return root(x) == root(y); }



  /// @brief

  /// $x$ を含む集合と $y$ を含む集合を併合する．

  /// 既に $x$ を含む集合と $y$ を含む集合が同じ集合である場合は false を返し，

  /// そうでない場合は true を返す．

  /// @complexity amortized $O(\\alpha(n))$

  bool unite(int x, int y) {

    x = root(x);

    y = root(y);

    if (x == y) return false;

    if (p[y] < p[x]) std::swap(x, y);

    if (p[x] == p[y]) --p[x];

    p[y] = x;

    return true;

  }

};



int main(void)

{

  int n;

  cin >> n;

  vector<pair<int,int>> xi,yi;

  vector<int> vx(n),vy(n);

  REP(i,n) {

    cin >> vx[i] >> vy[i];

    xi.emplace_back(vx[i], i);

    yi.emplace_back(vy[i], i);

  }

  sort(xi.begin(),xi.end());

  sort(yi.begin(),yi.end());

  vector<tuple<int,int,int>> edge;

  REP(i,n-1) {

    edge.emplace_back(xi[i+1].first - xi[i].first,

                      xi[i].second,

                      xi[i+1].second);

    edge.emplace_back(yi[i+1].first - yi[i].first,

                      yi[i].second,

                      yi[i+1].second);

  }

  sort(edge.begin(),edge.end());

  LL answer = 0ll;

  UnionFind uf(n);

  for(auto e:edge) {

    int cost, l, r;

    tie(cost, l, r) = e;

    if(uf.unite(l,r)) answer += cost;

  }

  cout << answer << endl;

  return 0;

}


// Fastest known solution for this problem: