#include<bits/stdc++.h>
#include<string.h>
 
typedef long long int ll;
#define all(x) (x).begin(), (x).end()
 
using namespace std;

int nxt() {
    int x;
    cin >> x;
    return x;
}

ll nxtl(){
    ll x;
    cin>>x;
    return x;
}
 
void SieveOfEratosthenes(int n,vector <int> &primes) 
{ 
    // Create a boolean array "prime[0..n]" and initialize 
    // all entries it as true. A value in prime[i] will 
    // finally be false if i is Not a prime, else true. 
    bool prime[n+1]; 
    memset(prime, true, sizeof(prime)); 
  
    for (int p=2; p*p<=n; p++) 
    { 
        // If prime[p] is not changed, then it is a prime 
        if (prime[p] == true) 
        { 
            // Update all multiples of p greater than or  
            // equal to the square of it 
            // numbers which are multiple of p and are 
            // less than p^2 are already been marked.  
            for (int i=p*p; i<=n; i += p) 
                prime[i] = false; 
        } 
    } 
  
    // Print all prime numbers 
    for (int p=2; p<=n; p++) 
       if (prime[p]) 
          primes.push_back(p);
} 
 
ll max(ll a,ll b)
{
    if(a>b)
        return a;
    return b;
}
 
ll power(ll x, ll y,ll mod) 
{ 
    ll temp; 
    if( y == 0) 
        return 1; 
    temp = power(x, y/2,mod); 
    if (y%2 == 0) 
        return (temp*temp)%mod; 
    else
        return (((x*temp)%mod)*temp)%mod; 
}

ll binomialCoeff(ll n, ll k) 
{ 
    ll C[k+1]; 
    memset(C, 0, sizeof(C)); 
  
    C[0] = 1;  // nC0 is 1 
  
    for (ll i = 1; i <= n; i++) 
    { 
        // Compute next row of pascal triangle using 
        // the previous row 
        for (ll j = min(i, k); j > 0; j--) 
            C[j] = C[j] + C[j-1]; 
    } 
    return C[k]; 
} 

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int n=nxt();
    vector <pair<int,int> > v(n);
    vector <pair<int,int> > v2(n);
    for(int i=0;i<n;i++){
        cin>>v[i].first>>v[i].second;
        v2[i]={v[i].second,v[i].first};
    }
    vector <pair<int,pair<pair<int,int>,pair<int,int> > > > edges;
    sort(all(v));
    sort(all(v2));
    ll test=0;
    for(int i=0;i<n-1;i++)
    {
        edges.push_back({v[i+1].first-v[i].first,{{v[i].first,v[i].second},{v[i+1].first,v[i+1].second}}});
        edges.push_back({v2[i+1].first-v2[i].first,{{v2[i].second,v2[i].first},{v2[i+1].second,v2[i+1].first}}});
    }
    sort(all(edges));
    int count=0;
    map <pair<int,int>,int> m1;
    map <int,vector <pair<int,int>> > m2;
    for(int i=0;i<n;i++)
    {
        if(m1.find({v[i].first,v[i].second})==m1.end())
        {
            m1[{v[i].first,v[i].second}]=i;
            m2[i].push_back({v[i].first,v[i].second});
        }
    }
    int m=edges.size();
    for(int i=0;i<m;i++)
    {
        int val=edges[i].first;
        auto p1=edges[i].second.first;
        auto p2=edges[i].second.second;
        //cout<<p1.first<<" "<<p1.second<<" "<<p2.first<<" "<<p2.second<<" "<<val<<endl;
        if(m1[p1]!=m1[p2])
        {//cout<<"came"<<endl;
            if(m2[m1[p1]].size()<=m2[m1[p2]].size())
            {
                vector <pair<int,int> > temp=m2[m1[p1]];
                int new_val=m1[p2];
                for(int j=0;j<temp.size();j++)
                {
                    m2[m1[p2]].push_back(temp[j]);
                    m1[temp[j]]=new_val;
                }
            }
            else
            {   
                vector <pair<int,int> > temp=m2[m1[p2]];
                int new_val=m1[p1];
                for(int j=0;j<temp.size();j++)
                {
                    m2[m1[p1]].push_back(temp[j]);
                    m1[temp[j]]=new_val;
                }

            }
            test+=val;
            count++;
        }
        if(count==n-1)
            break;
    }

    cout<<test<<endl;

    
    
    return 0;   
}




 
 


Measured memory_bytes: 27628