#pragma GCC optimize(3)
#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#include <algorithm>
#define maxn 200010
using namespace std;
int read()
{
	int x=0,w=1;
	char ch=getchar();
	while((ch>'9'||ch<'0')&&(ch!='-')) ch=getchar();
	if(ch=='-')
	{
		w=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*w;
}
int n;
struct Code
{
	int l,r,num;
}code[maxn];
struct Heap
{
	int l,r,num;
}heap[maxn];
priority_queue <int> q;
int ans,res;
bool cmpl(Code x,Code y)
{
	return x.l<y.l;
}
bool cmpr(Heap x,Heap y)
{
	return x.r<y.r;
}
int main()
{
	n=read();
	for(register int i=1;i<=n;i++)
	{
		code[i].l=read();
		code[i].r=read();
		code[i].num=i;
		heap[i].l=code[i].l;
		heap[i].r=code[i].r;
		heap[i].num=i;
	}
	sort(code+1,code+n+1,cmpl);
	sort(heap+1,heap+n+1,cmpr);
	if(code[n].l-code[1].l>code[n].r-code[1].r) q.push(code[n].l-code[1].l);
	if(code[n-1].l-code[1].l>code[n-1].r-code[1].r) q.push(code[n-1].l-code[1].l);
	if(code[n].l-code[2].l>code[n].r-code[2].r) q.push(code[n].l-code[2].l);
	if(heap[n].r-heap[1].r>=heap[n].l-heap[1].l) q.push(heap[n].r-heap[1].r);
	if(heap[n-1].r-heap[1].r>=heap[n-1].l-heap[1].l) q.push(heap[n-1].r-heap[1].r);
	if(heap[n].r-heap[2].r>=heap[n].l-heap[2].l) q.push(heap[n].r-heap[2].r);
	q.pop();
	cout<<q.top();
    return 0;
} 
#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstring>
#include <functional>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <queue>
#include <random>
#include <set>
#include <vector>
using namespace std;

template<typename A, typename B> ostream& operator<<(ostream &os, const pair<A, B> &p) { return os << '(' << p.first << ", " << p.second << ')'; }
template<typename T_container, typename T = typename enable_if<!is_same<T_container, string>::value, typename T_container::value_type>::type> ostream& operator<<(ostream &os, const T_container &v) { os << '{'; string sep; for (const T &x : v) os << sep << x, sep = ", "; return os << '}'; }

void dbg_out() { cerr << endl; }
template<typename Head, typename... Tail> void dbg_out(Head H, Tail... T) { cerr << ' ' << H; dbg_out(T...); }

#ifdef NEAL_DEBUG
#define dbg(...) cerr << "(" << #__VA_ARGS__ << "):", dbg_out(__VA_ARGS__)
#else
#define dbg(...)
#endif
#include <iostream>
#include <vector>
using namespace std;

struct union_find {
    // When data[x] < 0, x is a root and -data[x] is its tree size. When data[x] >= 0, data[x] is x's parent.
    vector<int> data;
    vector<int> edges;
    int components = 0;

    union_find(int n = -1) {
        if (n >= 0)
            init(n);
    }

    void init(int n) {
        data.assign(n + 1, -1);
        edges.assign(n + 1, 0);
        components = n;
    }

    int find(int x) {
        return data[x] < 0 ? x : data[x] = find(data[x]);
    }

    int get_size(int x) {
        return -data[find(x)];
    }

    bool unite(int x, int y) {
        x = find(x);
        y = find(y);

        if (x == y) {
            edges[x]++;
            return false;
        }

        if (-data[x] < -data[y])
            swap(x, y);

        data[x] += data[y];
        data[y] = x;
        edges[x] += edges[y];
        edges[x]++;
        components--;
        return true;
    }
};


const int V_MAX = 400005;

int main() {
    ios::sync_with_stdio(false);
#ifndef NEAL_DEBUG
    cin.tie(nullptr);
#endif

    int N;
    cin >> N;
    union_find UF(V_MAX);

    for (int i = 0; i < N; i++) {
        int a, b;
        cin >> a >> b;
        UF.unite(a, b);
    }

    int answer = 0;

    for (int i = 0; i < V_MAX; i++)
        if (UF.find(i) == i)
            answer += min(UF.get_size(i), UF.edges[i]);

    cout << answer << '\n';
}
