#include "bits/stdc++.h"
#define rep(i,n) for(int i = 0; i < n; i++)
typedef long long ll;
using namespace std;
#define llMAX numeric_limits<long long>::max()
#define intMAX numeric_limits<int>::max()
#define d_5 100000
#define d9_7 1000000007
#define vll vector<vector<long long>>
#define vl vector<long long>
#define vi vector<int>
#define vii vector<vector<int>>
#define pb push_back
#define pf push_front//UNION FIND
//構造体
struct Union{
  int num;
  int size;
};
int n;
Union par[d_5*5];//親の番号
//n要素で初期化
void init(int n){
  rep(i,n){
    par[i].num=i;
    par[i].size=1;
  }
}
//木の根を求める
int root(int x){
  if(par[x].num==x){
    return x;
  }else{
    return par[x].num=root(par[x].num);//経路圧縮
  }
}
//属する集合の要素数を求める
int size(int x){
  return par[root(x)].size;
}
//x,yが同じ集合に属するか否か
bool same(int x,int y){
  return (root(x)==root(y));
}
//x,yの属する集合を併合
void unite(int x,int y){
  x=root(x);
  y=root(y);
  if(x==y)return;
  par[y].size += par[x].size;
  par[x].num = y;
}
//集合の数を数える
int count(int n){
  set<int> cnt;
  //nは要素数
  rep(i,n){
    cnt.insert(root(i));
  }
  return cnt.size();
}//木構造グラフ
struct edge{
  struct treev* to;
  ll length;
};
struct treev{
  int num;
  vector<edge> nexts;
};
struct input{
  int u;
  int v;
  ll w;
};
bool cmp(const input &a, const input &b)//ソート用
{
  if(a.u!=b.u){
    return a.u<b.u;
  }else{
    return a.v<b.v;

  }
}
vector<treev*> vs;
vector<tuple<ll,int,int>> es;
treev* createtreenode(int num){
  treev* newt=new treev;
  if(newt==NULL){
    cout << "memory error" << endl;
    while(1){
      num=1;//メモリエラー対策
    }
  }
  newt->num=num;
  newt->nexts={};
  //cout << "fff" << endl;
  return newt;
}
void insertree(ll len,int Va,int Vb){//結ぶ頂点を
  len=(ll)abs(len);
  vs[Va]->nexts.push_back({vs[Vb],len});
  vs[Vb]->nexts.push_back({vs[Va],len});
  es.pb(make_tuple(len,Va,Vb));
  return;
}
void DFS(treev* root,ll depth,treev* pre){
  //cout << "eee" << endl;
  //ここにやりたい処理を書く
  for(auto i:root->nexts){
    if(pre==i.to){
      continue;
    }
    DFS(i.to,depth+i.length,root);
  }
  return;
}
deque<pair<treev*,ll>> opera;
void BFS(treev* pre){
  treev* root=opera.front().first;
  ll depth=opera.front().second;
  opera.pop_front();
  //cout << "eee" << endl;
  //ここにやりたい処理を書く
  for(auto i:root->nexts){
    if(pre==i.to){
      continue;
    }
    opera.push_back({i.to,depth+i.length});
  }
  BFS(root);
  return;
}
  

vector<bool> ness;//その頂点は必要かを考える
void DFS_ness(treev* root,ll depth,treev* pre,ll time,deque<treev*> path){
  //ゴールにちゃんと通じてるか調べる
  deque<treev*> tmppath=path;
  if(ness[root->num]==true||root->num==n-1){
    for(auto i:path){
      ness[i->num]=true;
    }
    //return;
  }

  for(auto i:root->nexts){
    if(ness[i.to->num]==true){
      for(auto i:path){
        ness[i->num]=true;
      }
      break;
    }
    if(ness[root->num]==true){
      return;
    }
    tmppath.pb(i.to);
    DFS_ness(i.to,depth+i.length,root,time + 1,tmppath);
    tmppath.pop_back();
  }
  return;
}
deque<deque<treev*>> loops;
void DFS_loop(treev* root,deque<treev*> path,vector<bool> past){
  //ループを調べる
  //pastの、0がunvisited,1がvisited
  deque<treev*> tmppath=path;
  if(ness[root->num]==true && past[root->num]==true){
    //ループを発見した際の処理
    int mode=0;
    deque<treev*> forrec;
    for(auto i=path.begin();i!=path.end();++i){
      if(mode==0){
        if((*i)->num==root->num){
          mode=1;
          forrec.pb(*i);
        }else{
          forrec.pb(*i);
        }
      }
    }
    loops.pb(forrec);
    return;
  }
  past[root->num]=true;//過去に訪れたことを記録
  for(auto i:root->nexts){
    if(ness[root->num]==false){//その頂点がゴールに通じてない場合
      continue;
    }
    tmppath.pb(i.to);
    DFS_loop(i.to,tmppath,past);
    tmppath.pop_back();
  }
  return;
}

//make_tuple(a,a,a)みたいに書くといい
int main(void){
  cin>>n;
  ll x;
  ll y;
  vector<pair<ll,int>> city1;
  vector<pair<ll,int>> city2;
  rep(i,n){
    cin>>x>>y;
    city1.pb({x,i});
    city2.pb({y,i});
  }
  sort(city1.begin(),city1.end());
  sort(city2.begin(),city2.end());
  rep(i,n){
    vs.push_back(createtreenode(i));
  }
  rep(i,n-1){
    insertree(city1[i+1].first-city1[i].first,city1[i+1].second,city1[i].second);
    insertree(city2[i+1].first-city2[i].first,city2[i+1].second,city2[i].second);
  }
  //cout<<"w";
  ll ans=0;
  //クラスカル法
  int cnt=0;//構成した辺の数
  int now=0;
  init(n);
  sort(es.begin(),es.end());
  //cout<<"e";
  while(cnt<n-1){
    if(same(get<1>(es[now]),get<2>(es[now]))!=true){
      unite(get<1>(es[now]),get<2>(es[now]));
      cnt++;
      ans+=get<0>(es[now]);
    }
    now++;
  }
  cout<<ans<<endl;
  return 0;
}

Measured memory_bytes: 22112