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

#define MAX_N 200200
#define MAX_M 400400

int vis[MAX_N], used[MAX_N], match[MAX_M], dist[MAX_N], n, m, ans;
queue<int> q;
vector<int> v[MAX_N];

void bfs(){
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(auto e : v[u]){
            if(match[e] && !dist[match[e]]){
                dist[match[e]] = dist[u]+1;
                q.push(match[e]);
            }
        }
    }
}

int dfs(int u){
    vis[u] = 1;
    for(auto e : v[u]){
        if(!match[e] || (!vis[match[e]] && dist[u]+1 == dist[match[e]] && dfs(match[e]))){
            match[e] = u;
            used[u] = 1;
            return 1;
        }
    }
    return 0;
}

int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        int a, b; scanf("%d %d",&a,&b);
        v[i].push_back(a), v[i].push_back(b);
    }
    while(1){
        for(int i=1;i<=n;i++) dist[i] = !used[i];
        for(int i=1;i<=n;i++) if(!used[i]) q.push(i);
        bfs();
        int f = 0;
        for(int i=1;i<=n;i++) vis[i] = 0;
        for(int i=1;i<=n;i++) if(!used[i]) f+=dfs(i);
        if(!f) break;
        ans += f;
    }
    printf("%d",ans);
}
#include <bits/stdc++.h>
//#include <atcoder/all>
using namespace std;
//using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)
#define rep2(i, s, n) for (int i = (s); i < (int)(n); i++)
//#define mydeb(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#define mydeb(fmt, ...) 

map<int,int>union_find_parent;
int union_find_add(int node);
int union_find_unite(int a,int b);
int union_find_root(int a);

int main()
{

	int n;
	cin >> n;
	vector<int> a(n),b(n);
	rep(i,n)
	{
		cin >> a[i] >> b[i];
	}

	map<int,int> numnode;
	map<int,int> is_cycle;

	rep(i,n)
	{
		if (union_find_root(a[i])<0)
		{
			mydeb("add %d\n",a[i]);
			union_find_add(a[i]);
			numnode[a[i]]=1;
			is_cycle[a[i]]=0;
		}
		if (union_find_root(b[i])<0)
		{
			mydeb("add %d\n",b[i]);
			union_find_add(b[i]);
			numnode[b[i]]=1;
			is_cycle[b[i]]=0;
		}

		int roota=union_find_root(a[i]);
		int rootb=union_find_root(b[i]);
		if (roota!=rootb)
		{
			mydeb("merge %d and %d\n",a[i],b[i]);
			union_find_unite(roota,rootb);
			// -> a's new root = b
			numnode[rootb]+=numnode[roota];
			is_cycle[rootb]|=is_cycle[roota];
			numnode.erase(roota);
			is_cycle.erase(roota);
		}
		else
		{
			// found cycle
			mydeb("cycle contains %d and %d\n",a[i],b[i]);
			is_cycle[roota]=1;
		}
	}

	int total=0;
	for (auto p: numnode)
	{
		total+=p.second-1;
	}
	for (auto p: is_cycle)
	{
		total+=p.second;
	}
	cout << total << endl;
}



int union_find_add(int node)
{
	assert(union_find_parent.count(node)==0);
	union_find_parent[node]=node;
	return 0;
}

int union_find_unite(int a,int b)
{
	assert( (union_find_parent.count(a)!=0) && (union_find_parent.count(b)!=0) );

	a=union_find_root(a);
	b=union_find_root(b);
	if (a!=b)
	{
		union_find_parent[a]=b;
	}
	return 0;
}

int union_find_root(int a)
{
	if (union_find_parent.count(a)==0)
	{
		return -1;
	}

	if (union_find_parent[a]==a)
	{
		return a;
	}
	else
	{
		return union_find_parent[a]=union_find_root(union_find_parent[a]);
	}
}

