PolandBall lives in a forest with his family. There are some trees in the forest. Trees are undirected acyclic graphs with k vertices and k - 1 edges, where k is some integer. Note that one vertex is a valid tree.

There is exactly one relative living in each vertex of each tree, they have unique ids from 1 to n. For each Ball i we know the id of its most distant relative living on the same tree. If there are several such vertices, we only know the value of the one with smallest id among those.

How many trees are there in the forest?

Input

The first line contains single integer n (1 ≤ n ≤ 104) — the number of Balls living in the forest.

The second line contains a sequence p1, p2, ..., pn of length n, where (1 ≤ pi ≤ n) holds and pi denotes the most distant from Ball i relative living on the same tree. If there are several most distant relatives living on the same tree, pi is the id of one with the smallest id.

It's guaranteed that the sequence p corresponds to some valid forest.

Hacking: To hack someone, you should provide a correct forest as a test. The sequence p will be calculated according to the forest and given to the solution you try to hack as input. Use the following format:

In the first line, output the integer n (1 ≤ n ≤ 104) — the number of Balls and the integer m (0 ≤ m < n) — the total number of edges in the forest. Then m lines should follow. The i-th of them should contain two integers ai and bi and represent an edge between vertices in which relatives ai and bi live. For example, the first sample is written as follows:
    
    
      
    5 3  
    1 2  
    3 4  
    4 5  
    

Output

You should output the number of trees in the forest where PolandBall lives.

Interaction

From the technical side, this problem is interactive. However, it should not affect you (except hacking) since there is no interaction.

Examples

Input

5
2 1 5 3 3

Output

2

Input

1
1


Output

1

Note

In the first sample testcase, possible forest is: 1-2 3-4-5. 

There are 2 trees overall.

In the second sample testcase, the only possible graph is one vertex and no edges. Therefore, there is only one tree.

Difficulty: EASY
Tags: Tree algorithms

Graph algorithms

Graph traversal

Spanning trees

Starter code:

Solutions:
n = int(input())
a = [0] + list(map(int, input().split()))
was = set()
for i in range(1, n + 1):
	if a[a[i]] == i:
		was.add(min(i, a[i]))
print(len(was))


from sys import stdin
input = stdin.readline
R = lambda : map(int, input().split())
I = lambda : int(input())

def find(x):
	while x != p[x]:
		p[x] = p[p[x]]
		x = p[x]
	return x
n = I()
v = []
t = 0
p = [0] + list(R())
for i in range(1, n + 1):
	v += (find(i),)
print(len(set(v)))


from sys import stdout, stdin, setrecursionlimit
from io import BytesIO, IOBase
from collections import *
from itertools import *
from bisect import *
from string import *
from queue import *
from heapq import *
from math import *
from re import *
from os import *

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = read(self._fd, max(fstat(self._fd).st_size, 8192))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = read(self._fd, max(fstat(self._fd).st_size, 8192))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(stdin, stdout) = (IOWrapper(stdin), IOWrapper(stdout))
(graph, mod, szzz) = ({}, 10 ** 9 + 7, lambda : sorted(zzz()))

def getStr():
	return input()

def getInt():
	return int(input())

def listStr():
	return list(input())

def getStrs():
	return input().split()

def isInt(s):
	return '0' <= s[0] <= '9'

def input():
	return stdin.readline().strip()

def zzz():
	return [int(i) for i in input().split()]

def output(answer, end='\n'):
	stdout.write(str(answer) + end)

def lcd(xnum1, xnum2):
	return xnum1 * xnum2 // gcd(xnum1, xnum2)
dx = [-1, 1, 0, 0, 1, -1, 1, -1]
dy = [0, 0, 1, -1, 1, -1, -1, 1]
daysInMounth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
n = getInt()
arr = [0] + zzz()
cnt = 0
for i in range(1, n + 1):
	if arr[arr[i]] == i and arr[i] <= i:
		cnt += 1
print(cnt)


n = int(input())
p = list(map(int, input().split()))
counter = 0
for i in range(n):
	if p[i] == i + 1:
		counter += 1
print((len(set(p)) - counter) // 2 + (len(set(p)) - counter) % 2 + counter)


import sys
n = int(input())
ps = [int(p) for p in input().split()]
num_trees = 0
ps_exself = set()
for (i, p) in enumerate(ps):
	if i + 1 != p:
		ps_exself.add(p)
	else:
		num_trees += 1
num_trees += len(ps_exself) // 2
ans = num_trees
print(ans)


import sys
input = sys.stdin.readline
n = int(input())
a = list(map(int, sys.stdin.readline().split()))
res = 0
d = {}
for i in range(n):
	if a[i] == -1:
		continue
	d = {}
	key1 = i + 1
	key2 = a[i]
	while key1 not in d:
		d[key1] = key2
		key1 = key2
		key2 = a[key1 - 1]
		if key2 == -1:
			d = {}
			break
	if len(list(d.keys())) > 0:
		res += 1
	for j in d:
		a[j - 1] = -1
print(res)


n = int(input())
p = list(map(int, input().split()))
cnt = 0
for i in range(n):
	if p[p[i] - 1] == i + 1 and p[i] <= i + 1:
		cnt += 1
print(cnt)


def solve(n, balls):
	marked = [False] * (n + 1)
	trees = 0
	for i in balls:
		if not marked[i]:
			trees += 1
			marked[i] = True
			marked[balls[i - 1]] = True
	return trees
n = int(input())
balls = list(map(int, input().split()))
print(solve(n, balls))


n = int(input())
a = list(map(int, input().split()))
ans = 0
b = []
for i in range(n):
	if a[i] == i + 1:
		ans += 1
	else:
		b.append(a[i])
print(ans + len(set(b)) // 2)


n = int(input())
p = list(map(int, input().split()))
nd = [set() for _ in range(n + 1)]
for i in range(n):
	nd[p[i]].add(i + 1)
	nd[i + 1].add(p[i])
tid = 0
t = [0] * (n + 1)
for i in range(1, n + 1):
	if t[i] > 0:
		continue
	tid += 1
	t[i] = tid
	tmp1 = nd[i]
	while len(tmp1) > 0:
		tmp2 = set()
		for x in tmp1:
			if t[x] > 0:
				continue
			t[x] = tid
			for y in nd[x]:
				tmp2.add(y)
		tmp1 = tmp2
print(tid)


n = int(input())
p = list(map(int, input().split()))
graph = [None] * n
for i in range(n):
	val_p = p[i] - 1
	if graph[i] == None:
		graph[i] = [val_p]
	else:
		graph[i].append(val_p)
	if graph[val_p] == None:
		graph[val_p] = [i]
	else:
		graph[val_p].append(i)
is_checked = [False] * n
ans = 0
for i in range(n):
	if not is_checked[i]:
		ans += 1
		stack = [i]
		while stack:
			top = stack.pop()
			is_checked[top] = True
			for val in set(graph[top]):
				if not is_checked[val]:
					stack.append(val)
print(ans)


read = lambda : map(int, input().split())
n = int(input())
a = list(read())
g = [list() for i in range(n + 1)]
for i in range(n):
	g[a[i]].append(i + 1)
	g[i + 1].append(a[i])
was = [0] * (n + 1)

def dfs(v):
	if was[v]:
		return
	was[v] = 1
	for u in g[v]:
		dfs(u)
cnt = 0
for i in range(1, n + 1):
	if not was[i]:
		dfs(i)
		cnt += 1
print(cnt)


n = int(input())
A = list(map(int, input().split()))
d = dict()
for j in range(n):
	if A[j] in d:
		d[A[j]].append(j + 1)
	else:
		d[A[j]] = [j + 1]
visited = [0] * n
ans = 0
for j in range(n):
	if visited[j] == 0:
		ans += 1
		visited[j] = 1
		rang = A[j]
		rang2 = A[rang - 1]
		if rang in d:
			for t in d[rang]:
				visited[t - 1] = 1
		if rang2 in d:
			for t in d[rang2]:
				visited[t - 1] = 1
print(ans)


def nTreesInForest(nPolandBalls, mostDistantRelatives):
	nTrees = 0
	forest = set()
	for iBall in range(nPolandBalls):
		(ball, itsRelative) = (iBall + 1, mostDistantRelatives[iBall])
		if ball == itsRelative:
			nTrees += 1
		elif itsRelative not in forest:
			forest.add(itsRelative)
	return nTrees + len(forest) // 2
print(nTreesInForest(int(input()), list(map(int, input().split()))))


from collections import *
import bisect
import heapq
import sys

def ri():
	return int(input())

def rl():
	return list(map(int, input().split()))

def dfs(v, current):
	components[v] = current
	to_visit = [v]
	while to_visit:
		node = to_visit.pop()
		for child in graph[node]:
			if not components[child]:
				components[child] = current
				to_visit.append(child)
n = ri()
pp = rl()
pp = [x - 1 for x in pp]
graph = [[] for i in range(n)]
for i in range(n):
	graph[i].append(pp[i])
	graph[pp[i]].append(i)
components = [0] * n
current = 0
for v in range(n):
	if not components[v]:
		current += 1
		dfs(v, current)
print(current)


import sys
import math
input = sys.stdin.readline
balls = int(input())
relations = list(map(int, input().split()))
sames = 0
for i in range(balls):
	if relations[i] == i + 1:
		sames += 1
		relations[i] = 'lolnoob'
relations = [i for i in relations if i != 'lolnoob']
print(int(math.ceil(len(list(set(relations))) / 2)) + sames)


n = int(input())
edge_list = list(map(int, input().split(' ')))
adj = [[] for i in range(n + 1)]
visited = [False for i in range(n + 1)]
for (idx, i) in enumerate(edge_list):
	p = i - 1
	adj[p].append(idx)
	adj[idx].append(p)

def dfs(node):
	if visited[node]:
		return
	visited[node] = True
	for ch in adj[node]:
		dfs(ch)
num_trees = 0
for i in range(n):
	if not visited[i]:
		num_trees += 1
		dfs(i)
print(num_trees)


from collections import defaultdict

def find_set(v):
	if v == parent[v]:
		return v
	parent[v] = find_set(parent[v])
	return parent[v]

def union_set(a, b):
	a = find_set(a)
	b = find_set(b)
	if a != b:
		parent[a] = b
n = int(input())
p = list(map(int, input().split()))
parent = [j for j in range(n + 1)]
for i in range(n):
	union_set(i + 1, p[i])
cost = 0
for i in range(1, n + 1):
	if i == parent[i]:
		cost += 1
print(cost)


from collections import Counter

class DisjointSetUnion:

	def __init__(self, n):
		self.parent = list(range(n))
		self.size = [1] * n
		self.num_sets = n

	def find(self, a):
		acopy = a
		while a != self.parent[a]:
			a = self.parent[a]
		while acopy != a:
			(self.parent[acopy], acopy) = (a, self.parent[acopy])
		return a

	def union(self, a, b):
		(a, b) = (self.find(a), self.find(b))
		if a != b:
			if self.size[a] < self.size[b]:
				(a, b) = (b, a)
			self.num_sets -= 1
			self.parent[b] = a
			self.size[a] += self.size[b]

	def set_size(self, a):
		return self.size[self.find(a)]

	def __len__(self):
		return self.num_sets

def pro(arr):
	n = len(arr)
	lst = []
	dsu = DisjointSetUnion(n)
	for i in range(n):
		k = arr[i] - 1
		dsu.union(i, k)
	return dsu.num_sets
t = 1
for i in range(t):
	n = int(input())
	arr = list(map(int, input().split()))
	res = pro(arr)
	print(res, flush=True)


n = int(input())
tree = list(range(n + 1))
arr = list(map(int, input().split()))

def find(st):
	if tree[st] != st:
		tree[st] = find(tree[st])
	return tree[st]
for i in range(n):
	tree[find(i + 1)] = find(arr[i])
ans = 0
for i in range(1, n + 1):
	if find(i) == i:
		ans += 1
print(ans)


import sys
readline = sys.stdin.readline

def i1():
	return int(readline())

def nl():
	return [int(s) for s in readline().split()]

def nn(n):
	return [int(readline()) for i in range(n)]

def nnp(n, x):
	return [int(readline()) + x for i in range(n)]

def nmp(n, x):
	return (int(readline()) + x for i in range(n))

def nlp(x):
	return [int(s) + x for s in readline().split()]

def nll(n):
	return [[int(s) for s in readline().split()] for i in range(n)]

def mll(n):
	return ([int(s) for s in readline().split()] for i in range(n))

def s1():
	return readline().rstrip()

def sl():
	return [s for s in readline().split()]

def sn(n):
	return [readline().rstrip() for i in range(n)]

def sm(n):
	return (readline().rstrip() for i in range(n))

def redir(s):
	global readline
	import os
	fn = sys.argv[0] + f'/../in-{s}.txt'
	readline = open(fn).readline if os.path.exists(fn) else readline
redir('c')
n = i1()
ids = set()
cnt = 0
i = 1
for p in nl():
	if i == p:
		cnt += 1
	else:
		ids.add(p)
	i += 1
print(cnt + len(ids) // 2)


P = [0]

def find(x):
	global P
	if P[x] == x:
		return x
	P[x] = find(P[x])
	return P[x]
n = int(input())
P = P + [0] * n
A = [0] + list(map(int, input().split()))
for i in range(1, n + 1):
	P[i] = i
for i in range(1, n + 1):
	u = find(A[i])
	v = find(i)
	P[u] = v
ans = 0
for i in range(1, n + 1):
	if P[i] == i:
		ans += 1
print(ans)


n = int(input())
a = [i for i in map(int, input().split())]
t = a[0]
t = a[t - 1]
p = [t, a[t - 1]]
d = {(min(p), max(p))}
for i in range(len(a)):
	t = a[i]
	t = a[t - 1]
	p = [t, a[t - 1]]
	p = (min(p), max(p))
	if not p in d:
		d.add(p)
print(len(d))


class CodeforcesTask755CSolution:

	def __init__(self):
		self.result = ''
		self.n = 0
		self.distant = []

	def read_input(self):
		self.n = int(input())
		self.distant = [int(x) for x in input().split(' ')]

	def process_task(self):
		graph = [[] for x in range(self.n)]
		for x in range(self.n):
			graph[x].append(self.distant[x])
			graph[self.distant[x] - 1].append(x + 1)
		comp = 0
		component = [0] * self.n
		for x in range(self.n):
			if not component[x]:
				comp += 1
				to_visit = [x + 1]
				while to_visit:
					visiting = to_visit.pop(-1)
					if not component[visiting - 1]:
						component[visiting - 1] = comp
						to_visit += graph[visiting - 1]
		self.result = str(comp)

	def get_result(self):
		return self.result
Solution = CodeforcesTask755CSolution()
Solution.read_input()
Solution.process_task()
print(Solution.get_result())


n = int(input()) + 1
p = list(range(n))

def g(x):
	if x != p[x]:
		p[x] = g(p[x])
	return p[x]
for (i, j) in enumerate(map(int, input().split()), 1):
	p[g(i)] = g(j)
for i in range(n):
	g(i)
print(len(set(p)) - 1)


import sys
noh = int(sys.stdin.readline())
loca = list(map(int, sys.stdin.readline().split()))
ap = set()
samecount = 0
for i in range(noh):
	if i + 1 not in ap and loca[i] not in ap:
		if i + 1 == loca[i]:
			samecount += 1
			ap.add(i + 1)
		elif loca[loca[i] - 1] == i + 1:
			ap.add(loca[i])
			ap.add(i + 1)
		else:
			ap.add(loca[loca[i] - 1])
			ap.add(loca[i])
if samecount >= 1:
	print((len(ap) - samecount) // 2 + samecount)
else:
	print(len(ap) // 2)


n = int(input())
p = list(map(int, input().split()))
gc = 0
fgc = 0
grps = [0] * (n + 1)
grpdic = {}
for i in range(n):
	a = grps[i + 1]
	b = grps[p[i]]
	if i + 1 == p[i]:
		fgc += 1
	elif a != 0 and b != 0:
		if a != b:
			fgc -= 1
			for e in grpdic[b]:
				grps[e] = a
	elif a == 0 and b == 0:
		gc += 1
		grps[i + 1] = grps[p[i]] = gc
		grpdic[gc] = set([i + 1, p[i]])
		fgc += 1
	elif a == 0:
		grps[i + 1] = b
		grpdic[b].add(i + 1)
	else:
		grps[p[i]] = a
		grpdic[a].add(p[i])
print(fgc)


from collections import defaultdict

class Graph:

	def __init__(self):
		self.graph = defaultdict(list)

	def addedge(self, u, v):
		self.graph[u].append(v)
		self.graph[v].append(u)

	def dfs(self, s):
		stack = []
		stack.append(s)
		while len(stack) != 0:
			a = stack[-1]
			stack.pop()
			if self.visited[a] == False:
				self.visited[a] = True
			for node in self.graph[a]:
				if self.visited[node] == False:
					stack.append(node)
		return

	def dfsutill(self):
		l = list(self.graph.keys())
		self.visited = [False for i in range(len(l) + 1)]
		c = 0
		for v in l:
			if self.visited[v] == False:
				c += 1
				self.dfs(v)
		return c
n = int(input())
l = list(map(int, input().split()))
g = Graph()
for i in range(1, n + 1):
	g.addedge(i, l[i - 1])
print(g.dfsutill())


import sys
import threading
from math import *
sys.setrecursionlimit(300000)
threading.stack_size(10 ** 8)

def main():

	def dfs(vertex, pp, used):
		used.add(vertex)
		for start in pp[vertex]:
			if start not in used:
				dfs(start, pp, used)
	used = set()
	s = 0
	n = int(input())
	p = [int(s) for s in input().split()]
	pp = [set() for s in range(n + 1)]
	for i in range(n):
		pp[p[i]].add(i + 1)
		pp[i + 1].add(p[i])
	for vertex in range(1, n + 1):
		if vertex not in used:
			dfs(vertex, pp, used)
			s += 1
	print(s)
t = threading.Thread(target=main)
t.start()
t.join()


n = int(input())
l = list(map(int, input().split()))
p = list(range(n + 1))
h = [0] * (n + 1)

def pred(x):
	if x != p[x]:
		p[x] = pred(p[x])
	return p[x]

def union(a, b):
	(a, b) = (pred(a), pred(b))
	if h[a] < h[b]:
		(a, b) = (b, a)
	p[b] = a
	if h[a] == h[b]:
		h[a] += 1
for i in range(1, n + 1):
	union(i, l[i - 1])
for i in range(n + 1):
	pred(i)
print(len(set(p)) - 1)


n = int(input())
trees = [int(_) for _ in input().split()]
ans = set()
tot = 0
for i in range(n):
	if trees[i] == i + 1:
		tot += 1
	else:
		ans.add(trees[i])
print(len(ans) // 2 + tot)


from sys import stdin
from collections import defaultdict
input = stdin.readline
T = 1
for t in range(1, T + 1):
	adj = defaultdict(lambda : list())
	n = int(input())
	edges = list(map(int, input().split()))
	for i in range(0, n):
		adj[i + 1].append(edges[i])
		adj[edges[i]].append(i + 1)
	visited = [False for x in range(n + 1)]
	count = 0
	for i in range(1, n + 1):
		if visited[i] == False:
			count += 1
			visited[i] = True
			q = []
			q.append(i)
			while len(q) != 0:
				parent = q.pop(0)
				for child in adj[parent]:
					if visited[child] == False:
						visited[child] = True
						q.append(child)
	print(count)


n = int(input())
b = list(map(int, input().strip().split()))
a = [False] * n
d = 0
while False in a:
	k = 0
	i = a.index(False)
	while a[i] == False:
		a[i] = True
		i = b[i] - 1
		k += 1
	if k == 1 and b[i] - 1 != i:
		continue
	else:
		d += 1
print(d)


class UnionFind:

	def __init__(self, n):
		self.up_bound = list(range(n))
		self.rank = [0] * n

	def find(self, x_index):
		if self.up_bound[x_index] == x_index:
			return x_index
		self.up_bound[x_index] = self.find(self.up_bound[x_index])
		return self.up_bound[x_index]

	def union(self, x_index, y_index):
		repr_x = self.find(x_index)
		repr_y = self.find(y_index)
		if repr_x == repr_y:
			return False
		if self.rank[repr_x] == self.rank[repr_y]:
			self.rank[repr_x] += 1
			self.up_bound[repr_y] = repr_x
		elif self.rank[repr_x] > self.rank[repr_y]:
			self.up_bound[repr_y] = repr_x
		else:
			self.up_bound[repr_x] = repr_y
		return True
n = int(input())
aa = list(map(int, input().split()))
aa = [x - 1 for x in aa]
uu = UnionFind(n)
nb_components = n
for i in range(n):
	u = i
	v = aa[i]
	regroup = uu.union(u, v)
	if regroup:
		nb_components -= 1
print(nb_components)


num_balls = int(input())
p = [0]
p.extend(list(map(int, input().split())))
solution = 0
for from_ball in range(1, num_balls + 1):
	to_ball = p[from_ball]
	if to_ball == from_ball:
		solution += 2
	elif p[to_ball] == from_ball:
		solution += 1
print(int(solution / 2))


def trees(array):
	num_trees = 0
	checked = {}
	for i in array:
		if i not in checked:
			new_tree = []
			curr_vert = i
			while curr_vert not in new_tree and curr_vert not in checked:
				new_tree.append(curr_vert)
				curr_vert = array[curr_vert - 1]
			if curr_vert in checked:
				for vert in new_tree:
					checked[vert] = checked[curr_vert]
			else:
				num_trees += 1
				for vert in new_tree:
					checked[vert] = num_trees
	return num_trees
n = int(input())
p = list(map(int, input().split()))
print(trees(p))


n = int(input())
arr = list(map(int, input().split()))
ans = 0
edges = set([])
for (i, vertex) in enumerate(arr):
	if vertex == i + 1:
		ans += 1
	else:
		edges.add(vertex)
print(ans + len(edges) // 2)


n = int(input())
vertices = list(map(int, input().split(' ')))
graph = {}
visited = {}

def dfs(node):
	if visited.get(node, 0):
		return
	visited[node] = 1
	for i in graph[node]:
		dfs(i)
for i in range(n):
	if not i + 1 in graph:
		graph[i + 1] = []
	graph[i + 1].append(vertices[i])
	if not vertices[i] in graph:
		graph[vertices[i]] = []
	graph[vertices[i]].append(i + 1)
components = 0
for i in range(1, n + 1):
	if i not in visited:
		components += 1
		dfs(i)
print(components)


def nTreesInForest(nPolandBalls, mostDistantRelatives):
	nLoners = sum((str(iBall + 1) == mostDistantRelatives[iBall] for iBall in range(nPolandBalls)))
	return (nLoners + len(set(mostDistantRelatives))) // 2
print(nTreesInForest(int(input()), input().split()))


from math import *
import threading
import sys
from collections import defaultdict
from pprint import pprint
sys.setrecursionlimit(300000)
mod = 10 ** 9
inf = 10 ** 15
yes = 'YES'
no = 'NO'
import os
import sys
from io import BytesIO, IOBase
BUFSIZE = 8192

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None

	def read(self):
		while True:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = os.read(self._fd, max(os.fstat(self._fd).st_size, BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def npr(n, r):
	return factorial(n) // factorial(n - r) if n >= r else 0

def ncr(n, r):
	return factorial(n) // (factorial(r) * factorial(n - r)) if n >= r else 0

def lower_bound(li, num):
	answer = -1
	start = 0
	end = len(li) - 1
	while start <= end:
		middle = (end + start) // 2
		if li[middle] >= num:
			answer = middle
			end = middle - 1
		else:
			start = middle + 1
	return answer

def upper_bound(li, num):
	answer = -1
	start = 0
	end = len(li) - 1
	while start <= end:
		middle = (end + start) // 2
		if li[middle] <= num:
			answer = middle
			start = middle + 1
		else:
			end = middle - 1
	return answer

def abs(x):
	return x if x >= 0 else -x

def binary_search(li, val):
	ans = -1
	lb = 0
	ub = len(li) - 1
	while lb <= ub:
		mid = (lb + ub) // 2
		if li[mid] > val:
			ub = mid - 1
		elif val > li[mid]:
			lb = mid + 1
		else:
			ans = mid
			break
	return ans

def kadane(x):
	sum_so_far = 0
	current_sum = 0
	for i in x:
		current_sum += i
		if current_sum < 0:
			current_sum = 0
		else:
			sum_so_far = max(sum_so_far, current_sum)
	return sum_so_far

def pref(li):
	pref_sum = [0]
	for i in li:
		pref_sum.append(pref_sum[-1] + i)
	return pref_sum

def SieveOfEratosthenes(n):
	prime = [True for i in range(n + 1)]
	p = 2
	li = []
	while p * p <= n:
		if prime[p] == True:
			for i in range(p * p, n + 1, p):
				prime[i] = False
		p += 1
	for p in range(2, len(prime)):
		if prime[p]:
			li.append(p)
	return li

def primefactors(n):
	factors = []
	while n % 2 == 0:
		factors.append(2)
		n //= 2
	for i in range(3, int(sqrt(n)) + 1, 2):
		while n % i == 0:
			factors.append(i)
			n //= i
	if n > 2:
		factors.append(n)
	return factors

def prod(li):
	ans = 1
	for i in li:
		ans *= i
	return ans

def dist(a, b):
	d = abs(a[1] - b[1]) + abs(a[2] - b[2])
	return d

def power_of_n(x, n):
	cnt = 0
	while x % n == 0:
		cnt += 1
		x //= n
	return cnt
for _ in range(1):
	n = int(input())
	a = list(map(int, input().split()))
	adj = {}
	for i in range(1, n + 1):
		if i in adj:
			adj[i] += [a[i - 1]]
		else:
			adj[i] = [a[i - 1]]
		if a[i - 1] in adj:
			adj[a[i - 1]] += [i]
		else:
			adj[a[i - 1]] = [i]
	vis = [0] * n

	def dfs(n):
		vis[n - 1] = 1
		for child in adj[n]:
			if vis[child - 1] == 0:
				dfs(child)
	cnt = 0
	for i in range(n):
		if vis[i] == 0:
			dfs(i + 1)
			cnt += 1
	print(cnt)


import sys

def solve():
	n = int(input())
	p = [int(pi) - 1 for pi in input().split()]
	iso = 0
	exist = [False] * n
	for (i, pi) in enumerate(p):
		if i == pi:
			iso += 1
		else:
			exist[pi] = True
	ans = iso + sum(exist) // 2
	print(ans)
solve()


def nTreesInForest(PolandBalls, mostDistantRelatives):
	return (sum((ball == itsRelative for (ball, itsRelative) in zip(PolandBalls, mostDistantRelatives))) + len(set(mostDistantRelatives))) // 2
print(nTreesInForest(range(1, int(input()) + 1), list(map(int, input().split()))))


def root(k):
	while a[k][0] != k:
		k = a[k][0]
	return a[k][0]

def merge(k, l):
	s1 = root(k)
	s2 = root(l)
	if s1 == s2:
		return 0
	else:
		if a[s1][1] > a[s2][1]:
			a[s2][0] = s1
			a[s1][1] += a[s2][1]
		else:
			a[s1][0] = s2
			a[s2][1] += a[s1][1]
		return 1
n = int(input())
a = [0] + [[i, 1] for i in range(1, n + 1)]
b = [0 for i in range(n + 1)]
s = [''] + input().split()
for i in range(1, n + 1):
	b[i] = int(s[i])
ans = n
for i in range(1, n + 1):
	ans -= merge(i, b[i])
print(ans)


n = int(input())
p = [-1] + list(map(int, input().split()))
a = [-1] * (n + 1)
o = 0
for i in range(1, n + 1):
	if p[i] == i:
		o += 1
	elif a[p[i]] == -1 == a[i]:
		o += 1
		a[p[p[i]]] = 99
		a[p[i]] = 99
print(o)


import sys
import math
try:
	sys.stdin = open('input.txt', 'r')
	sys.stdout = open('output.txt', 'w')
except:
	pass

class DisjointSet:

	def __init__(self, n):
		self.parent = [0]
		for i in range(1, n + 1):
			self.parent.append(i)

	def find(self, node):
		if node != self.parent[node]:
			self.parent[node] = self.find(self.parent[node])
		return self.parent[node]

	def union(self, u, v):
		leader_u = self.find(u)
		leader_v = self.find(v)
		if leader_u != leader_v:
			self.parent[leader_u] = leader_v
n = int(input())
relatives = [int(x) for x in input().split()]
ds = DisjointSet(n)
edge_index = [i for i in range(n + 1)]
for (i, r) in enumerate(relatives):
	ds.union(edge_index[i + 1], r)
trees = 0
seen = set()
for i in range(1, n + 1):
	leader = ds.find(i)
	if leader not in seen:
		seen.add(leader)
		trees += 1
print(trees)


n = int(input())
a = list(map(int, input().split()))
ans = 0
cnt = [0] * (10 ** 4 + 1)
for i in range(0, n):
	if a[i] == i + 1:
		ans += 1
		continue
	cnt[a[i]] = 1
print(sum(cnt) // 2 + ans)


def root(x):
	if parent[x] == x:
		return x
	return root(parent[x])

def connect(x, y):
	x_root = root(x)
	y_root = root(y)
	if depth[x_root] < depth[y_root]:
		parent[x_root] = y_root
		depth[y_root] += 1
	else:
		parent[y_root] = x_root
		depth[x_root] += 1
n = int(input())
p = list(map(int, input().split()))
parent = [i for i in range(n + 1)]
depth = [1] * (n + 1)
for i in range(n):
	connect(i + 1, p[i])
tree_cnt = 0
for i in range(1, n + 1):
	if parent[i] == i:
		tree_cnt += 1
print(tree_cnt)


import sys
n = int(input())
p = list(map(lambda x: int(x) - 1, sys.stdin.readline().split()))
c = 0
s = set()
for i in range(n):
	if i == p[i]:
		c += 1
	else:
		s.add(p[i])
print(c + len(s) // 2)


from collections import *

def ri():
	return map(int, input().split())

def bfs(s, adj, v):
	if v[s] == 1:
		return 0
	q = deque()
	q.append(s)
	v[s] = 1
	while q:
		n = q.popleft()
		for a in adj[n]:
			if v[a] == 0:
				q.append(a)
				v[a] = 1
	return 1
n = int(input())
m = list(ri())
adj = [set() for i in range(n + 1)]
for i in range(1, n + 1):
	adj[i].add(m[i - 1])
	adj[m[i - 1]].add(i)
v = [0 for i in range(n + 1)]
c = 0
for i in range(1, n + 1):
	c += bfs(i, adj, v)
print(c)


def main():
	n = int(input())
	seq = list(map(int, input().split()))
	distinct = []
	ans = 0
	for i in range(n):
		if seq[i] == i + 1:
			ans += 1
			continue
		if seq[i] not in distinct:
			distinct.append(seq[i])
	ans += int(len(distinct) / 2)
	print(ans)
main()


def solve():
	n = int(input())
	S = [int(x) - 1 for x in input().split()]
	k = len([x for (i, x) in enumerate(S) if i == x])
	return k + (len(set(S)) - k) // 2
print(solve())


class DisjointSet:

	def __init__(self, n):
		self.num_sets = n
		self.parents = list(range(n))
		self.ranks = [0] * n
		self.sizes = [1] * n

	def find(self, j):
		while self.parents[j] != j:
			jj = self.parents[j]
			self.parents[j] = self.parents[jj]
			j = jj
		return j

	def union(self, i, j):
		r0 = self.find(i)
		r1 = self.find(j)
		if r0 == r1:
			return False
		rd = self.ranks[r0] - self.ranks[r1]
		if rd == 0:
			self.ranks[r0] += 1
		elif rd < 0:
			(r0, r1) = (r1, r0)
		self.parents[r1] = r0
		self.sizes[r0] += self.sizes[r1]
		self.sizes[r1] = 0
		self.num_sets -= 1
		return True
n = int(input())
tl = list(map(int, input().split()))
dsu = DisjointSet(n)
for (i, t) in enumerate(tl):
	dsu.union(i, t - 1)
print(dsu.num_sets)


def dfs(x):
	if not A[x]:
		A[x] = True
		for i in B[x]:
			dfs(i)
N = int(100000.0) + 10
A = [False] * N
B = [list([]) for i in range(N)]
n = int(input())
t = [int(i) - 1 for i in input().split()]
for i in range(n):
	B[i].append(t[i])
	B[t[i]].append(i)
ans = 0
for i in range(n):
	if not A[i]:
		dfs(i)
		ans += 1
print(ans)


import sys
import re
import math
import itertools
import collections
import bisect
mod = 1000000007
pi = 3.141592653589793
IS = float('inf')
xy = [(1, 0), (-1, 0), (0, 1), (0, -1)]
bs = [(-1, -1), (-1, 1), (1, 1), (1, -1)]

def niten(a, b):
	return abs(a - b) if a >= 0 and b >= 0 else a + abs(b) if a >= 0 else abs(a) + b if b >= 0 else abs(abs(a) - abs(b))

def fib(n):
	return [(seq.append(seq[i - 2] + seq[i - 1]), seq[i - 2])[1] for seq in [[0, 1]] for i in range(2, n)]

def gcd(a, b):
	return a if b == 0 else gcd(b, a % b)

def lcm(a, b):
	return a * b / gcd(a, b)

def eucl(x1, y1, x2, y2):
	return ((x1 - x2) ** 2 + (y1 - y2) ** 2) ** 0.5

def choco(xa, ya, xb, yb, xc, yc, xd, yd):
	return 1 if abs((yb - ya) * (yd - yc) + (xb - xa) * (xd - xc)) < 1e-10 else 0

def pscl(num, l=[1]):
	for i in range(num):
		l = map(lambda x, y: x + y, [0] + l, l + [0])
	return l
import heapq
n = int(input())
l = [int(i) for i in input().split()]
syu = {}
ich = []
d = {i for i in range(1, n + 1)}
for (a, i) in enumerate(l):
	if i in syu:
		syu[i] += 1
	else:
		syu[i] = 1
		heapq.heappush(ich, i)
while len(ich):
	tmp = heapq.heappop(ich)
	if tmp < l[tmp - 1]:
		syu[tmp] += syu[l[tmp - 1]]
		syu[l[tmp - 1]] = 0
ans = 0
for i in syu:
	if syu[i] > 0:
		ans += 1
print(ans)


n = int(input())
a = list(map(int, input().split()))
f = list(range(n + 1))

def fd(a):
	(rt, tmp) = (a, a)
	while f[rt] != rt:
		rt = f[rt]
	while f[a] != a:
		tmp = f[a]
		f[a] = rt
		a = tmp
	return rt

def un(a, b):
	(ra, rb) = (fd(a), fd(b))
	if ra == rb:
		return
	else:
		f[ra] = rb
for i in range(n):
	un(i + 1, a[i])
print(sum((1 for i in range(1, n + 1) if f[i] == i)))


n = int(input())
p = list(map(int, input().split()))
c = set()
k = 0
for (i, v) in enumerate(p):
	if i + 1 != v:
		c.add(v)
	else:
		k += 1
print(len(c) // 2 + k)


n = int(input())
a = [int(i) for i in input().split()]
z = 0
for i in range(n):
	if a[i] == i + 1:
		z += 1
print((len(set(a)) - z) // 2 + (len(set(a)) - z) % 2 + z)


import sys
input = sys.stdin.readline
n = int(input())
balls = list(map(int, input().split()))
ans = 0
reached = set()
for i in range(n):
	tree = {i + 1, balls[i], balls[balls[i] - 1]}
	if tree.isdisjoint(reached):
		ans += 1
	reached |= tree
print(ans)


def Get(parent, x):
	if parent[x] == x:
		return x
	parent[x] = Get(parent, parent[x])
	return parent[x]

def Union(parent, rank, a, b):
	a = Get(parent, a)
	b = Get(parent, b)
	if rank[a] == rank[b]:
		rank[a] += 1
	if rank[a] < rank[b]:
		parent[a] = b
	else:
		parent[b] = a
n = int(input())
arr = list(map(int, input().split()))
count = 0
parent = [i for i in range(n + 1)]
rank = [0 for i in range(n + 1)]
for i in range(n):
	u = i + 1
	v = arr[i]
	pu = Get(parent, u)
	pv = Get(parent, v)
	if pu != pv:
		Union(parent, rank, pu, pv)
for i in range(1, n + 1):
	if parent[i] == i:
		count += 1
print(count)


def main():
	MAXN = int(10000.0 + 7)
	par = []
	for i in range(MAXN):
		par.append(i)

	def find(i):
		if i == par[i]:
			return i
		return find(par[i])

	def union(i, j):
		p = find(i)
		q = find(j)
		if p == q:
			return
		par[p] = q
	n = int(input())
	p = list(map(int, input().split(' ')))
	for i in range(n):
		union(i, p[i] - 1)
	s = set()
	for i in range(n):
		s.add(find(i))
	print(len(s))
main()


import random

class DSU:

	def __init__(self, n):
		self.n = n
		self.comps = n
		self.parent = [i for i in range(n)]

	def FIND(self, x):
		if x != self.parent[x]:
			self.parent[x] = self.FIND(self.parent[x])
		return self.parent[x]

	def UNION(self, x, y):
		xRoot = self.FIND(x)
		yRoot = self.FIND(y)
		if xRoot != yRoot:
			self.comps -= 1
			if random.randint(1, 2) & 1:
				self.parent[xRoot] = yRoot
			else:
				self.parent[yRoot] = xRoot
			return 1
		return 0
n = int(input())
balls = [int(x) for x in input().split()]
dsu = DSU(n)
for (ind, val) in enumerate(balls):
	dsu.UNION(ind, val - 1)
print(dsu.comps)


import sys
input = sys.stdin.readline

def main():
	n = int(input())
	ar = []
	for x in input().split():
		ar.append(int(x) - 1)
	g = [[] for _ in range(n)]
	alone = 0
	stuff = 0
	for i in range(n):
		g[ar[i]].append(i)
	for i in range(n):
		if g[i] != []:
			if g[i] == [i]:
				alone += 1
			else:
				stuff += 1
	print(stuff // 2 + alone)
main()


n = int(input())
p = list(map(int, input().split()))
f = [0 for _ in range(n)]
g = [[] for _ in range(n)]
for i in range(n):
	g[i].append(p[i] - 1)
	g[p[i] - 1].append(i)
ans = 0

def dfs(x):
	f[x] = 1
	for i in g[x]:
		if not f[i]:
			dfs(i)
for i in range(n):
	if not f[i]:
		ans += 1
		dfs(i)
print(ans)


n = int(input())
l = list(map(int, input().split()))
al = [[] for i in range(n + 1)]
for i in range(n):
	a = i + 1
	b = l[i]
	al[a].append(b)
	al[b].append(a)
c = 0
va = [0] * (n + 1)

def dfs(n):
	va[n] = 1
	for e in al[n]:
		if va[e] == 0:
			dfs(e)
for e in range(1, n + 1):
	if va[e] == 0:
		c = c + 1
		dfs(e)
print(c)


n = int(input())
p = list(map(int, input().split()))
for i in range(n):
	p[i] -= 1
ans = 0
for i in range(n):
	if p[i] == i:
		ans += 2
	elif p[p[i]] == i:
		ans += 1
print(ans // 2)
print(ans, file=__import__('sys').stderr)


n = int(input())
p = list(map(lambda x: int(x) - 1, input().split()))

def find(x):
	while x != p[x]:
		p[x] = p[p[x]]
		x = p[x]
for i in range(n):
	find(i)
print(len(set(p)))


class DisjointSet:

	def __init__(self):
		self.parent = {}
		self.cnt = {}

	def make_set(self, u):
		self.parent[u] = u
		self.cnt[u] = 1

	def find_parent(self, u):
		if u != self.parent[u]:
			self.parent[u] = self.find_parent(self.parent[u])
		return self.parent[u]

	def union(self, u, v):
		u = self.find_parent(u)
		v = self.find_parent(v)
		if u != v:
			if self.cnt[u] > self.cnt[v]:
				self.parent[v] = self.parent[u]
				self.cnt[v] = self.cnt[u] + 1
			else:
				self.parent[u] = self.parent[v]
				self.cnt[u] = self.cnt[v] + 1
n = int(input())
ar = list(map(int, input().split()))
S = DisjointSet()
for i in range(n):
	S.make_set(i + 1)
for i in range(n):
	S.union(i + 1, ar[i])
print(len(set((S.find_parent(i + 1) for i in range(n)))))


n = int(input())
p = input().split()
done = []
singles = 0
doubles = 0
for x in range(0, n):
	xx = int(p[x]) - 1
	if xx not in done:
		done.append(xx)
		if x == xx:
			singles = singles + 1
		else:
			doubles = doubles + 1
	else:
		continue
print(int(doubles / 2 + singles))


n = int(input())
s = [int(i) for i in input().split()]
ans = n
r = set()
for i in range(n):
	if s[i] != i + 1:
		if s[i] in r:
			ans -= 1
		else:
			r.add(s[i])
			r.add(s[s[i] - 1])
print(ans)


import sys
inf = float('inf')
mod = 1000000007

def get_array():
	return list(map(int, sys.stdin.readline().split()))

def get_ints():
	return map(int, sys.stdin.readline().split())

def input():
	return sys.stdin.readline()

def main():
	n = int(input())
	arr = get_array()
	ans = 0
	s = set()
	for i in range(n):
		if arr[i] == i + 1:
			ans += 1
		else:
			s.add(arr[i])
	print(ans + len(s) // 2)
main()


n = int(input())
p = [0] + list(map(int, input().split()))
d = set()
for i in range(1, n + 1):
	if p[p[i]] == i:
		d.add(min(i, p[i]))
print(len(d))


from collections import *

def ri():
	return int(input())

def rl():
	return list(map(int, input().split()))

def dfs(start, visited, current):
	visited[start] = current
	to_visit = [start]
	while to_visit:
		node = to_visit.pop()
		for child in graph[node]:
			if not visited[child]:
				visited[child] = current
				to_visit.append(child)

def count_components():
	n = len(graph)
	visited = [0] * n
	nb_components = 0
	for v in range(n):
		if not visited[v]:
			nb_components += 1
			dfs(v, visited, nb_components)
	return nb_components
n = ri()
pp = rl()
pp = [x - 1 for x in pp]
graph = [[] for i in range(n)]
for i in range(n):
	graph[i].append(pp[i])
	graph[pp[i]].append(i)
print(count_components())


n = int(input())
x = [[] for _ in range(n + 1)]
p = [-1 for _ in range(n + 1)]
a = [-1] + list(map(int, input().split()))
last = 0
ans = 0
for i in range(1, n + 1):
	if p[i] == -1 and p[a[i]] == -1:
		p[i] = p[a[i]] = last
		x[last] += [i, a[i]]
		last += 1
	elif p[i] != -1 and p[a[i]] != -1:
		if p[i] != p[a[i]]:
			before = p[i]
			for e in x[p[i]]:
				p[e] = p[a[i]]
				x[p[a[i]]] += [e]
			x[before] = []
	elif p[i] != -1 and p[a[i]] == -1:
		p[a[i]] = p[i]
		x[p[i]] += [a[i]]
	else:
		p[i] = p[a[i]]
		x[p[a[i]]] += [i]
for i in range(last):
	if x[i] != []:
		ans += 1
print(ans)


from collections import defaultdict, Counter
from math import sqrt, log10, log2, log, gcd, floor, factorial
from bisect import bisect_left, bisect_right
from itertools import combinations, combinations_with_replacement
import sys, io, os
input = sys.stdin.readline
mod = 10 ** 9 + 7
inf = float('inf')

def get_list():
	return [int(i) for i in input().split()]
yn = lambda a: print('YES' if a else 'NO')
ceil = lambda a, b: (a + b - 1) // b

class DisjointSetUnion:

	def __init__(self, l=[]):
		self.parent = defaultdict(int)
		self.size = defaultdict(lambda : 1)
		self.num_sets = 0
		for i in l:
			self.add(i)

	def add(self, val):
		if val not in self.parent:
			self.parent[val] = val
			self.size[val] = 1
			self.num_sets += 1

	def find(self, a):
		acopy = a
		while a != self.parent[a]:
			a = self.parent[a]
		while acopy != a:
			(self.parent[acopy], acopy) = (a, self.parent[acopy])
		return a

	def union(self, a, b):
		(a, b) = (self.find(a), self.find(b))
		if a != b:
			if self.size[a] < self.size[b]:
				(a, b) = (b, a)
			self.num_sets -= 1
			self.parent[b] = a
			self.size[a] += self.size[b]
			return 1
		return 0

	def set_size(self, a):
		return self.size[self.find(a)]

	def __len__(self):
		return self.num_sets
t = 1
for i in range(t):
	n = int(input())
	l = [0] + get_list()
	seta = DisjointSetUnion([i for i in range(1, n + 1)])
	for i in range(1, n + 1):
		seta.union(i, l[i])
	print(len(seta))


class DisjointSet(object):

	def __init__(self):
		self.parent = {}
		self.rank = {}

	def make_set(self, v):
		self.parent[v] = v
		self.rank[v] = 0

	def find_set(self, v):
		if v != self.parent[v]:
			self.parent[v] = self.find_set(self.parent[v])
		return self.parent[v]

	def union_sets(self, a, b):
		a = self.find_set(a)
		b = self.find_set(b)
		if a != b:
			if self.rank[a] < self.rank[b]:
				(a, b) = (b, a)
			self.parent[b] = a
			if self.rank[a] == self.rank[b]:
				self.rank[a] += 1

def solve():
	n = int(input())
	neighbours = map(int, input().split())
	s = DisjointSet()
	for i in range(n):
		s.make_set(i + 1)
	for (i, nei) in enumerate(neighbours):
		s.union_sets(i + 1, nei)
	return len(set((s.find_set(i + 1) for i in range(n))))
print(solve())


from sys import stdin
k = int(input())
forest = [int(elem) for elem in stdin.readline().rstrip().split()]
graph = {}
unique = set()
for i in range(0, k):
	id = i + 1
	tree = forest[i]
	if id not in graph:
		graph[id] = [tree]
	graph[id].append(tree)
edge_count = 0
for id in graph:
	for tree in graph[id]:
		if id == tree:
			continue
		elif (id, tree) not in unique and (tree, id) not in unique:
			edge_count += 1
			unique.add((id, tree))
			unique.add((tree, id))
if k > 1:
	print(k - edge_count)
else:
	print(k)


def main():
	n = int(input())
	p = [-1] + [int(c) for c in input().split()]
	ans = 0
	for (i, j) in enumerate(p):
		if j >= i and p[j] == i:
			ans += 1
	print(ans)
main()


n = int(input())
p = list(map(int, input().split(' ')))
adj_list = [[] for i in range(n + 1)]
v = [0] * (n + 1)
for i in range(n):
	adj_list[i + 1].append(p[i])
	adj_list[p[i]].append(i + 1)
tree = 0
for i in range(1, n + 1):
	if v[i] == 0:
		s = [i]
		while len(s) > 0:
			x = s.pop()
			v[x] = 1
			for j in adj_list[x]:
				if v[j] == 0:
					s.append(j)
		tree += 1
print(tree)


n = int(input())
v = [None] + [int(k) for k in input().split()]
p = [k for k in range(n + 1)]
ans = n

def anc(i):
	if i == p[i]:
		return p[i]
	p[i] = anc(p[i])
	return p[i]

def join(x, y):
	global ans
	px = anc(x)
	py = anc(y)
	if px != py:
		p[px] = py
		ans -= 1
for i in range(1, n + 1):
	join(i, v[i])
print(ans)


from collections import deque
n = int(input())
p = [0] + list(map(int, input().split(' ')))
g = [-1] * (n + 1)
b = 0
u = [[] for i in range(n + 1)]
for i in range(1, n + 1):
	u[i].append(p[i])
	u[p[i]].append(i)
for i in range(1, n + 1):
	if g[i] == -1:
		b += 1
		g[i] = b
		q = deque([i])
		while len(q) > 0:
			x = q.popleft()
			for el in u[x]:
				if g[el] == -1:
					q.append(el)
					g[el] = b
print(b)


input()
l = [int(i) for i in input().split()]
ans = 0
w = set([])
for (a, i) in enumerate(l):
	if a + 1 == i:
		ans += 1
	else:
		w.add(i)
print(ans + len(w) // 2)


visited = []

def dfs(adj, s):
	visited[s] = True
	for i in adj[s]:
		if not visited[i]:
			dfs(adj, i)
n = int(input())
a = list(map(int, input().split()))
adj = []
for i in range(n + 5):
	adj.append([])
	visited.append(False)
for i in range(n):
	adj[i + 1].append(a[i])
	adj[a[i]].append(i + 1)
ans = 0
for i in range(1, n + 1):
	if not visited[i]:
		ans += 1
		dfs(adj, i)
print(ans)


from collections import defaultdict
n = int(input())
l = list(map(int, input().split()))
d = set({})
s = defaultdict(set)
t = 0
for i in range(n):
	s[i + 1].add(l[i])
	s[l[i]].add(i + 1)
visited = [False] * n

def DFS(s, i):
	stack = [i]
	while len(stack):
		current = stack.pop()
		visited[current - 1] = True
		for j in s[current]:
			if visited[j - 1] == False:
				stack.append(j)
count = 0
for i in s:
	if visited[i - 1] == False:
		DFS(s, i)
		count = count + 1
print(count)


n = int(input())
count = 0
mas = [int(s) for s in input().split()]
for i in range(n):
	if i + 1 == mas[i]:
		count += 1
		mas[i] = 0
mas = set(mas)
flag = 0 in mas
print(count + (len(mas) - flag) // 2)


class DisjointSet:

	def __init__(self, N):
		self._N = N
		self._rank = N * [0]
		self._parent = list(range(N))

	def find(self, i):
		if self._parent[i] != i:
			self._parent[i] = self.find(self._parent[i])
		return self._parent[i]

	def union(self, x, y):
		px = self.find(x)
		py = self.find(y)
		if self._rank[px] < self._rank[py]:
			self._parent[px] = py
		else:
			if self._rank[px] == self._rank[py]:
				self._rank[px] += 1
			self._parent[py] = px
N = int(input())
nbrs = list(map(int, input().strip().split()))
for i in range(N):
	nbrs[i] -= 1
cc = DisjointSet(N)
for i in range(N):
	cc.union(i, nbrs[i])
result = set([cc.find(i) for i in range(N)])
print(len(result))


n = int(input())
A = [int(i) for i in input().split()]
candidate_set = set()
additional_count = 0
for i in range(n):
	if i + 1 == A[i]:
		additional_count += 1
	else:
		candidate_set.add(A[i])
print(len(candidate_set) // 2 + additional_count)


def root(a):
	while parent[a] != a:
		parent[a] = parent[parent[a]]
		a = parent[a]
	return a

def union(a, b):
	a = root(a)
	b = root(b)
	if a == b:
		return
	if size[a] <= size[b]:
		parent[a] = b
		size[b] += size[a]
		size[a] = 0
	else:
		parent[b] = a
		size[a] += size[b]
		size[b] = 0
n = int(input())
parent = [i for i in range(n + 1)]
size = [1] * (n + 1)
a = list(map(int, input().split()))
for i in range(n):
	union(a[i], i + 1)
tree = 0
for e in set(parent[1:]):
	if size[e] > 0:
		tree += 1
print(tree)


def merge(id1, id2):
	if id1 != id2:
		if len(dsu[id1]) < len(dsu[id2]):
			(id1, id2) = (id2, id1)
		for j in dsu.pop(id2):
			gid[j] = id1
			dsu[id1].append(j)
gid = {}
dsu = {}
n = int(input())
s = list(map(int, input().split()))
for i in range(n):
	gid[i] = i
	dsu[i] = [i]
	s[i] -= 1
for i in range(n):
	merge(gid[i], gid[s[i]])
print(len(dsu))


vertex_num = int(input())
graph_map = []
for _ in range(vertex_num + 1):
	graph_map.append([])
visited = [0] * (vertex_num + 1)
neighbors = list(map(int, input().split()))
for (i, node) in enumerate(neighbors):
	if not node in graph_map[i + 1]:
		graph_map[i + 1].append(node)
	if not i + 1 in graph_map[node]:
		graph_map[node].append(i + 1)
q = []
components = 0
for (i, node) in enumerate(graph_map):
	if i == vertex_num:
		break
	if not visited[i + 1]:
		components += 1
		q.append(i + 1)
		visited[i] = 1
		while len(q) > 0:
			n = q.pop(0)
			for e in graph_map[n]:
				if not visited[e]:
					visited[e] = 1
					q.append(e)
print(components)


def dfs(forest, key, visited):
	visited.add(key)
	for i in forest[key]:
		if i not in visited:
			dfs(forest, i, visited)
n = int(input())
a = input().split()
forest = dict()
res = 0
for (i, j) in enumerate(a):
	if str(i + 1) not in forest:
		forest[str(i + 1)] = [j]
	else:
		forest[str(i + 1)] += [j]
	if j not in forest:
		forest[j] = [str(i + 1)]
	else:
		forest[j] += [str(i + 1)]
visited = set()
for i in range(len(a)):
	key = str(i + 1)
	if key not in visited:
		dfs(forest, key, visited)
		res += 1
print(res)


from collections import defaultdict
n = int(input())
l = list(map(int, input().split()))
d = defaultdict(list)
for i in range(n):
	d[l[i]].append(i + 1)
k = 0
for i in d:
	if len(d[i]) == 1 and d[i][0] == i:
		k += 1
s = len(set(l))
print((k + s) // 2)


from collections import Counter
from collections import defaultdict
import math
import random
import heapq as hq
from math import sqrt
import sys

def input():
	return sys.stdin.readline().strip()

def iinput():
	return int(input())

def tinput():
	return input().split()

def rinput():
	return map(int, tinput())

def rlinput():
	return list(rinput())
mod = int(1000000000.0) + 7

def root(parent, a):
	while parent[a] != a:
		parent[a] = parent[parent[a]]
		a = parent[a]
	return a

def union(parent, a, b):
	(root_a, root_b) = (root(parent, a), root(parent, b))
	if root_a == root_b:
		return
	parent[root_a] = root_b

def solve(adj, colors, n):
	pass
n = iinput()
a = rlinput()
parent = list(range(n))
for i in range(n):
	union(parent, i, a[i] - 1)
s = set()
for i in range(n):
	s.add(root(parent, i))
print(len(s))


n = int(input())
l = list(map(int, input().split()))
ans = 0
arr = [0] * 10004
for i in range(n):
	if l[i] == i + 1:
		ans -= -1
		arr[l[i]] = 1
l = list(set(l))
c = 0
for i in l:
	if arr[i] == 0:
		c -= -1
print(ans + c // 2)


import sys
inf = float('inf')
(mod, MOD) = (1000000007, 998244353)

def get_array():
	return list(map(int, sys.stdin.readline().strip().split()))

def get_ints():
	return map(int, sys.stdin.readline().strip().split())

def input():
	return sys.stdin.readline().strip()

class DisjointSetUnion:

	def __init__(self, n):
		self.parent = list(range(n))
		self.size = [1] * n
		self.num_sets = n

	def find(self, a):
		acopy = a
		while a != self.parent[a]:
			a = self.parent[a]
		while acopy != a:
			(self.parent[acopy], acopy) = (a, self.parent[acopy])
		return a

	def union(self, a, b):
		(a, b) = (self.find(a), self.find(b))
		if a != b:
			if self.size[a] < self.size[b]:
				(a, b) = (b, a)
			self.num_sets -= 1
			self.parent[b] = a
			self.size[a] += self.size[b]

	def set_size(self, a):
		return self.size[self.find(a)]

	def __len__(self):
		return self.num_sets
n = int(input())
Arr = get_array()
obj = DisjointSetUnion(n)
for i in range(n):
	obj.union(Arr[i] - 1, i)
myset = set()
for i in range(n):
	myset.add(obj.find(i))
print(len(myset))


(n, P) = (int(input()), list(map(int, input().split())))
print(sum((P[P[i] - 1] == i + 1 and P[i] < i + 2 for i in range(n))))


class SNM2:

	def __init__(self):
		self.parent = dict()
		self.rank = dict()

	def find(self, e):
		parent_e = self.parent.get(e)
		if parent_e is None:
			return None
		if e == parent_e:
			return e
		else:
			res = self.find(parent_e)
			self.parent[e] = res
			return res

	def make_set(self, e, parent=None):
		self.parent[e] = e if parent is None else parent
		self.rank[e] = 0

	def union(self, x, y):
		px = self.find(x)
		py = self.find(y)
		if px == py:
			return
		if self.rank[px] < self.rank[py]:
			self.parent[px] = py
		else:
			self.parent[py] = px
			if self.rank[px] == self.rank[py]:
				self.rank[px] += 1
n = int(input())
ns = list(map(int, input().split()))
snm = SNM2()
for i in range(1, n + 1):
	snm.make_set(i)
for (k, n) in enumerate(ns, 1):
	snm.union(k, n)
roots = set()
for k in snm.parent:
	roots.add(snm.find(k))
print(len(roots))


import sys
readline = sys.stdin.readline

def i1():
	return int(readline())

def nl():
	return [int(s) for s in readline().split()]

def nn(n):
	return [int(readline()) for i in range(n)]

def nnp(n, x):
	return [int(readline()) + x for i in range(n)]

def nmp(n, x):
	return (int(readline()) + x for i in range(n))

def nlp(x):
	return [int(s) + x for s in readline().split()]

def nll(n):
	return [[int(s) for s in readline().split()] for i in range(n)]

def mll(n):
	return ([int(s) for s in readline().split()] for i in range(n))

def s1():
	return readline().rstrip()

def sl():
	return [s for s in readline().split()]

def sn(n):
	return [readline().rstrip() for i in range(n)]

def sm(n):
	return (readline().rstrip() for i in range(n))

def redir(s):
	global readline
	import os
	fn = sys.argv[0] + f'/../in-{s}.txt'
	readline = open(fn).readline if os.path.exists(fn) else readline
redir('c')
n = i1()
fa = list(range(n + 1))

def find(i):
	if i == fa[i]:
		return i
	fa[i] = find(fa[i])
	return fa[i]

def merge(i, p):
	i = find(i)
	p = find(p)
	if i != p:
		fa[i] = p
for (i, p) in enumerate(nl(), start=1):
	merge(i, p)
print(len([i for i in range(1, n + 1) if fa[i] == i]))


input()
p = [int(x) for x in input().split()]
(s, count) = (set(), 0)
for (i, v) in enumerate(p):
	if i + 1 == v:
		count += 1
	else:
		s.add(v)
print(count + len(s) // 2)


def main():
	n = int(input())
	relav = list(map(int, input().split()))
	trees = dict.fromkeys(range(1, n + 1), -1)
	for i in range(n):
		if trees[relav[i]] == -1:
			trees[i + 1] = relav[i]
		else:
			trees[i + 1] = trees[relav[i]]
	count = 0
	for i in range(1, n + 1):
		if trees[i] == i:
			count += 1
	print(count)
main()


(n, p) = (int(input()), [-1] + list(map(int, input().split())))
print(sum((1 for (i, pi) in enumerate(p) if pi >= i and p[pi] == i)))


from collections import defaultdict

class Graph:

	def __init__(self, n):
		self.nodes = n
		self.graph = defaultdict(list)
		self.visited = [0] * self.nodes

	def add_edge(self, x, y):
		self.graph[x].append(y)

	def count_components(self):
		ret = 0
		for i in range(self.nodes):
			if self.visited[i] == 0:
				ret += 1
				self.dfs(i, ret)
		return ret

	def dfs(self, u, c):
		self.visited[u] = c
		for v in self.graph[u]:
			if self.visited[v] == 0:
				self.dfs(v, c)
n = int(input())
ar = list(map(int, input().split()))
graph = Graph(n)
for i in range(n):
	graph.add_edge(i, ar[i] - 1)
	graph.add_edge(ar[i] - 1, i)
print(graph.count_components())


def dfs(v, child, visited):
	visited[v] = True
	for i in child[v]:
		if visited[i] == False:
			dfs(i, child, visited)

def main():
	n = int(input())
	child = {i: [] for i in range(1, n + 1)}
	lis = list(map(int, input().split()))
	for i in range(n):
		child[lis[i]].append(i + 1)
		child[i + 1].append(lis[i])
	visited = {i: False for i in range(1, n + 1)}
	ans = 0
	for i in range(1, n + 1):
		if visited[i] == False:
			ans += 1
			dfs(i, child, visited)
	print(ans)
main()


import sys
import math
import heapq
import bisect
from collections import Counter
from collections import defaultdict
from io import BytesIO, IOBase
import string

class FastIO(IOBase):
	newlines = 0

	def __init__(self, file):
		import os
		self.os = os
		self._fd = file.fileno()
		self.buffer = BytesIO()
		self.writable = 'x' in file.mode or 'r' not in file.mode
		self.write = self.buffer.write if self.writable else None
		self.BUFSIZE = 8192

	def read(self):
		while True:
			b = self.os.read(self._fd, max(self.os.fstat(self._fd).st_size, self.BUFSIZE))
			if not b:
				break
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines = 0
		return self.buffer.read()

	def readline(self):
		while self.newlines == 0:
			b = self.os.read(self._fd, max(self.os.fstat(self._fd).st_size, self.BUFSIZE))
			self.newlines = b.count(b'\n') + (not b)
			ptr = self.buffer.tell()
			(self.buffer.seek(0, 2), self.buffer.write(b), self.buffer.seek(ptr))
		self.newlines -= 1
		return self.buffer.readline()

	def flush(self):
		if self.writable:
			self.os.write(self._fd, self.buffer.getvalue())
			(self.buffer.truncate(0), self.buffer.seek(0))

class IOWrapper(IOBase):

	def __init__(self, file):
		self.buffer = FastIO(file)
		self.flush = self.buffer.flush
		self.writable = self.buffer.writable
		self.write = lambda s: self.buffer.write(s.encode('ascii'))
		self.read = lambda : self.buffer.read().decode('ascii')
		self.readline = lambda : self.buffer.readline().decode('ascii')
(sys.stdin, sys.stdout) = (IOWrapper(sys.stdin), IOWrapper(sys.stdout))
input = lambda : sys.stdin.readline().rstrip('\r\n')

def get_int():
	return int(input())

def get_ints():
	return list(map(int, input().split(' ')))

def get_int_grid(n):
	return [get_ints() for _ in range(n)]

def get_str():
	return input().split(' ')

def yes_no(b):
	if b:
		return 'YES'
	else:
		return 'NO'

def binary_search(good, left, right, delta=1, right_true=False):
	limits = [left, right]
	while limits[1] - limits[0] > delta:
		if delta == 1:
			mid = sum(limits) // 2
		else:
			mid = sum(limits) / 2
		if good(mid):
			limits[int(right_true)] = mid
		else:
			limits[int(~right_true)] = mid
	if good(limits[int(right_true)]):
		return limits[int(right_true)]
	else:
		return False

def prefix_sums(a, drop_zero=False):
	p = [0]
	for x in a:
		p.append(p[-1] + x)
	if drop_zero:
		return p[1:]
	else:
		return p

def prefix_mins(a, drop_zero=False):
	p = [float('inf')]
	for x in a:
		p.append(min(p[-1], x))
	if drop_zero:
		return p[1:]
	else:
		return p

class DSU:

	def __init__(self, nodes):
		self.p = [i for i in range(nodes)]
		self.r = [0 for i in range(nodes)]
		self.m = [i for i in range(nodes)]
		self.M = [i for i in range(nodes)]
		self.s = [1 for i in range(nodes)]

	def get(self, u):
		if u != self.p[u]:
			self.p[u] = self.get(self.p[u])
		return self.p[u]

	def union(self, u, v):
		u = self.get(u)
		v = self.get(v)
		if u != v:
			if self.r[u] > self.r[v]:
				(u, v) = (v, u)
			self.p[u] = v
			if self.r[u] == self.r[v]:
				self.r[v] += 1
			self.m[v] = min(self.m[u], self.m[v])
			self.M[v] = max(self.M[u], self.M[v])
			self.s[v] += self.s[u]

def solve_c():
	n = get_int()
	dsu = DSU(n)
	p = get_ints()
	for (i, x) in enumerate(p):
		dsu.union(i, x - 1)
	return sum([dsu.get(i) == i for i in range(n)])
print(solve_c())


from collections import Counter as cntr
from math import inf

def cin():
	return map(int, input().split(' '))

def dfs(graph, src):
	global visited
	for v in graph[src]:
		if visited[v] == False:
			visited[v] = True
			dfs(graph, v)
n = int(input())
p = list(cin())
tree = {i: [] for i in range(n)}
for i in range(n):
	tree[i].append(p[i] - 1)
	tree[p[i] - 1].append(i)
visited = [False for i in range(n)]
count = 0
for i in range(n):
	if visited[i] == False:
		count += 1
		visited[i] = True
		dfs(tree, i)
print(count)


from sys import stdin, stdout
n = int(stdin.readline())
pairs = list(map(int, stdin.readline().split()))
visit = [0 for i in range(n + 1)]
vertices = [[] for i in range(n + 1)]

def dfs(v):
	visit[v] = 1
	for u in vertices[v]:
		if not visit[u]:
			dfs(u)
for i in range(n):
	vertices[i + 1].append(pairs[i])
	vertices[pairs[i]].append(i + 1)
ans = 0
for i in range(1, n + 1):
	if not visit[i]:
		dfs(i)
		ans += 1
stdout.write(str(ans))


from sys import *
from decimal import *

def gcd(a, b):
	if b:
		return gcd(b, a % b)
	return a
n = int(input())
arr = [int(z) for z in input().split()]
s = set(arr)
res = 0
for i in range(n):
	if arr[i] == i + 1:
		res += 1
res += (len(s) - res) // 2
print(res)


def nTreesInForest(PolandBalls, mostDistantRelatives):
	nTrees = 0
	forest = set()
	for iBall in PolandBalls:
		(ball, itsRelative) = (iBall + 1, mostDistantRelatives[iBall])
		if ball == itsRelative:
			nTrees += 1
		elif itsRelative not in forest:
			nTrees += 0.5
			forest.add(itsRelative)
	return round(nTrees)
print(nTreesInForest(range(int(input())), tuple(map(int, input().split()))))


def dint(x):
	return int(x) - 1
n = int(input())
far = map(dint, input().split())
links = [None] * n
pairs = list(enumerate(far))
pairs.sort(key=lambda x: x[1])
curr = 0
for (i, e) in pairs:
	if links[e] is None:
		links[e] = curr
		curr += 1
	links[i] = links[e]
print(curr)


from collections import defaultdict

class Graph:

	def __init__(self):
		self.graph = defaultdict(list)

	def addEdge(self, fr, to):
		self.graph[fr].append(to)
		self.graph[to].append(fr)

	def goAndMark(self, start, visited):
		visited[start - 1] = True
		for x in self.graph[start]:
			if visited[x - 1] == False:
				self.goAndMark(x, visited)

	def solve(self, visited, n):
		count = 0
		for j in range(n):
			if visited[j] == False:
				self.goAndMark(j + 1, visited)
				count += 1
		return count
n = int(input())
List = [int(x) for x in input().split()]
G = Graph()
for i in range(n):
	G.addEdge(i + 1, List[i])
visited = [False] * n
print(G.solve(visited, n))


n = int(input())
x = [int(i) for i in input().split()]
s = set()
sum = 0
for i in range(n):
	if i + 1 == x[i]:
		sum += 1
	else:
		s.add(x[i])
print(sum + len(s) // 2)


from collections import defaultdict
n = int(input())
tree = defaultdict(set)
for (i, p) in enumerate(map(int, input().split())):
	tree[i].add(p - 1)
	tree[p - 1].add(i)
unvisited = set(range(n))

def DFS(curr):
	for nei in tree[curr]:
		if nei in unvisited:
			unvisited.remove(nei)
			DFS(nei)
ans = 0
while unvisited:
	ans += 1
	start = unvisited.pop()
	DFS(start)
print(ans)


n = int(input())
a = [int(i) - 1 for i in input().split()]
ans = 0
cnt = [0] * n
for i in range(n):
	if a[i] == i:
		ans += 1
		continue
	cnt[a[i]] = 1
print(sum(cnt) // 2 + ans)


n = int(input())
g = [int(i) - 1 for i in input().split()]
s = set()
cnt = 0
for i in range(n):
	if i == g[i]:
		cnt += 1
	else:
		s.add(g[i])
print(cnt + len(s) // 2)


def main():
	n = int(input())
	p = [int(c) - 1 for c in input().split()]
	links = [None] * n
	pairs = list(enumerate(p))
	pairs.sort(key=lambda x: x[1])
	curr = 0
	for (i, e) in pairs:
		if links[e] is None:
			links[e] = curr
			curr += 1
		links[i] = links[e]
	print(curr)
main()


def dfs(node, parent):
	global a, visited
	visited[node] = True
	for i in range(len(a[node])):
		if a[node][i] != parent:
			dfs(a[node][i], node)
n = int(input())
l = 0
b = list(map(int, input().split()))
a = [[] for _ in range(n + 1)]
visited = [False] * (n + 1)
for i in range(n):
	a[i + 1].append(b[i])
	a[b[i]].append(i + 1)
for i in range(1, n + 1):
	if visited[i] == True:
		continue
	else:
		dfs(i, 0)
		l = l + 1
print(l)


from sys import stdin
input = lambda : stdin.readline().strip()
from math import ceil, sqrt, factorial, gcd

def traversal(a):
	if a in z:
		return
	z.add(a)
	for k in d[a]:
		if len(d[k]) > 0:
			traversal(k)
		else:
			z.add(k)
n = int(input())
d = {i: set() for i in range(1, n + 1)}
l = list(map(int, input().split()))
for i in range(n):
	d[i + 1].add(l[i])
	d[l[i]].add(i + 1)
for i in d:
	if len(d[i]) > 0:
		z = set()
		traversal(i)
		for k in z:
			if k != i:
				d[k] = set()
count = 0
for i in d:
	if len(d[i]) > 0:
		count += 1
print(count)


def find(a, x):
	while 1:
		if a[x] < 0:
			return x
		else:
			x = a[x]
n = int(input())
a = [-1] * (n + 1)
ed = [0] + [int(y) for y in input().split(' ')]
for i in range(1, 1 + n):
	p1 = find(a, i)
	p2 = find(a, ed[i])
	if p1 != p2:
		a[p1] += a[p2]
		a[p2] = p1
c = 0
for j in range(1, 1 + n):
	if a[j] < 0:
		c = c + 1
print(c)


import sys
n = int(sys.stdin.readline().strip())
A = [int(i) for i in sys.stdin.readline().strip().split(' ')]
leaves = set()
count_tree = 0
for i in A:
	v1 = A[i - 1]
	if v1 not in leaves:
		leaves.add(v1)
		v2 = A[v1 - 1]
		leaves.add(v2)
		count_tree = count_tree + 1
	else:
		continue
print(count_tree)


from collections import *

def find(visited, a, i):
	while a[i] != i:
		a[i] = a[a[i]]
		i = a[i]
n = int(input())
a = [int(i) - 1 for i in input().split()]
visited = [False] * n
tot = 0
for i in range(n):
	if not visited[i]:
		find(visited, a, i)
print(len(Counter(a)))


n = int(input())
gr = [[] for i in range(n)]
for (v, to) in enumerate(map(int, input().split())):
	to -= 1
	gr[v].append(to)
	gr[to].append(v)

def dfs(v):
	used[v] = True
	for to in gr[v]:
		if not used[to]:
			dfs(to)
used = [False] * n
answer = 0
for v in range(n):
	if not used[v]:
		answer += 1
		dfs(v)
print(answer)


n = int(input())
p = list(map(lambda x: int(x) - 1, input().split()))
r = [None] * n
for i in range(n):
	v = p[i]
	u = p[v]
	r[i] = min(v, u)
print(len(set(r)))