#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>

#define ll long long
#define ull unsigned long long
#define ld long double
#define F first
#define S second
#define eb emplace_back
#define pb push_back
#define mp make_pair
#define all(x) begin(x), end(x)
#define rall(x) rbegin(x), rend(x)

using namespace __gnu_pbds;
using namespace std;

template <typename T> using oset = tree <pair <T, T>, null_type, less <pair <T, T>>, rb_tree_tag, tree_order_statistics_node_update>;

template <typename T>
void ckmin(T& a, const T b) {a = min(a, b);}

template <typename T>
void ckmax(T& a, const T b) {a = max(a, b);}

template <typename T>
vector <T> sieve(T n) {
    vector <T> f(n+1, 1), res;
    for (T i = 2; i <= n; ++i)
        for (T j = i; j <= n; j += i)
            ++f[j];
    for (T i = 2; i <= n; ++i)
        if (f[i] == 2)
            res.eb(i);
    return res;
}

template <typename T>
T power(T a, T b, const T c) {
    a %= c;
    T res = 1;
    while (b)
    {
        if (b & 1) res = res * a % c;
        b >>= 1;
        a = a * a % c;
    }
    return res;
}

template <typename T>
void print(vector <T> v) {
    for (T i : v) cout << i << " ";
    cout << '\n';
}

template <typename T>
void print(vector <vector <T>>& v) {
    for (vector <T>& vv : v) {
        for (T& i : vv)
            cout << i << " ";
        cout << '\n';
    }
}

template <typename T>
void read(vector <T>& v) {for (T& i : v) cin >> i;}

template <typename T>
void read(T&& t) {cin >> t;}

template <typename T, typename... Args>
void read(T&& t, Args&&... args)
{
    cin >> t;
    read(forward<Args>(args)...);
}

template <typename T>
void print(T&& t) {cout << t << '\n';}

template <typename T, typename... Args>
void print(T&& t, Args&&... args)
{
    cout << t << " ";
    print(forward<Args>(args)...);
}

void usaco(string name = "")
{
    ios_base::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    if(name.size())
    {
        freopen((name+".in").c_str(), "r", stdin);
        freopen((name+".txt").c_str(), "w", stdout);
    }
}

int n, edge, node;
const int mxn = 4e5+1;
vector <int> adj[mxn];
bool vis[mxn];

void dfs(int u)
{
    vis[u] = 1;
    ++node;
    for (int v : adj[u])
    {
        ++edge;
        if (!vis[v])
            dfs(v);
    }
}

inline void solve()
{
    cin >> n;
    for (int i = 0; i < n; ++i)
    {
        int u, v;
        cin >> u >> v;
        adj[u].emplace_back(v);
        adj[v].emplace_back(u);
    }
    long long ans = 0;
    for (int i = 1; i < mxn; ++i)
    {
        if (vis[i] || adj[i].empty()) continue;
        edge = node = 0;
        dfs(i);
        edge >>= 1;
        ans += min(edge, node);
    }
    cout << ans << '\n';
}

int32_t main()
{
    usaco();
    int t = 1;
    while (t--) solve();
}

#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define drep(i, cc, n) for (ll i = (cc); i <= (n); ++i)
#define rep(i, n) drep(i, 0, n - 1)
#define P pair<ll, ll>
#define all(a) (a).begin(), (a).end()
#define pb push_back
#define fi first
#define se second

ll n;
vector<vector<ll>> G(400001);
vector<bool> loop_dupl(400001, false);
vector<bool> check(400001, false);
ll ans = 0;
bool dfs(ll color, ll parent){
    //cout << "color : " << color << endl;
    if(check[color]) return true;
    check[color] = true;
    ans++;
    bool ret = loop_dupl[color];
    for(ll link : G[color]) if(link != parent) ret |= dfs(link, color);
    //cout << ret << endl;
    return ret;
}

int main(){
    cin >> n;
    
    ll a, b, c;
    set<P> s; 
    rep(i, n){
        cin >> a >> b;
        if(a > b){
            c = a; a = b; b = c;
        }
        if(s.find(make_pair(a, b)) == s.end()){
            s.insert(make_pair(a, b));
            G[a].pb(b);
            G[b].pb(a);
        }else{
            loop_dupl[a] = true;
            loop_dupl[b] = true;
        }
    }

    for(ll i=1; i<=400000; i++){
        if(check[i]) continue;
        //cout << "aaa" << endl;
        ans -= !dfs(i, 0);
    }

    cout << ans << endl;
}