CodeEff/ECCO huggingface dataset:
# Original solution
class UnionFind():
  def __init__(self, n):
    self.n = n
    self.parents = [-1] * n

  def find(self, x):
    if self.parents[x] < 0:
      return x
    else:
      self.parents[x] = self.find(self.parents[x])
    return self.parents[x]

  def union(self, x, y):
    x = self.find(x)
    y = self.find(y)

    if x == y:
      return

    if self.parents[x] > self.parents[y]:
      x, y = y, x

    self.parents[x] += self.parents[y]
    self.parents[y] = x

  def same(self, x, y):
    return self.find(x) == self.find(y)

  def roots(self):
    return [i for i, x in enumerate(self.parents) if x < 0]

  def num_roots(self):
    return len([i for i, x in enumerate(self.parents) if x < 0])

  def members(self, x):
    root = self.find(x)
    return [i for i in range(self.n) if self.find(i) == root]

  def num_members(self,x):
    return abs(self.parents[self.find(x)])

  def __str__(self):
    return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N, M = list(map(int, input().split()))
P = list(map(int, input().split()))
uf = UnionFind(N)
for _ in range(M):
  x,y = list(map(int, input().split()))
  uf.union(x-1,y-1)
from collections import defaultdict
d = defaultdict(lambda: [])
for i in range(N):
  d[uf.find(i)].append(i)
from bisect import bisect_left
def binary_search(A,p):
  A = sorted(A)
  if A[0]<=p and p<=A[-1]:
    if p == A[bisect_left(A,p)]:
      return True
  return False
ans = 0
for v in list(d.values()):
  for a in v:
    if binary_search(v,P[a]-1):
      ans += 1
print(ans)


# Optimized solution
class UnionFind():
  def __init__(self, n):
    self.n = n
    self.parents = [-1] * n

  def find(self, x):
    if self.parents[x] < 0:
      return x
    else:
      self.parents[x] = self.find(self.parents[x])
    return self.parents[x]

  def union(self, x, y):
    x = self.find(x)
    y = self.find(y)

    if x == y:
      return

    if self.parents[x] > self.parents[y]:
      x, y = y, x

    self.parents[x] += self.parents[y]
    self.parents[y] = x

  def same(self, x, y):
    return self.find(x) == self.find(y)

  def roots(self):
    return [i for i, x in enumerate(self.parents) if x < 0]

  def num_roots(self):
    return len([i for i, x in enumerate(self.parents) if x < 0])

  def members(self, x):
    root = self.find(x)
    return [i for i in range(self.n) if self.find(i) == root]

  def num_members(self,x):
    return abs(self.parents[self.find(x)])

  def __str__(self):
    return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N, M = list(map(int, input().split()))
P = list(map(int, input().split()))
uf = UnionFind(N)
for _ in range(M):
  x,y = list(map(int, input().split()))
  uf.union(x-1,y-1)
from collections import defaultdict
d = defaultdict(lambda: [])
for i in range(N):
  d[uf.find(i)].append(i)
from bisect import bisect_left
def binary_search(A,p):
  if A[0]<=p and p<=A[-1]:
    if p == A[bisect_left(A,p)]:
      return True
  return False
ans = 0
for v in list(d.values()):
  lis = sorted(v)
  for a in v:
    if binary_search(lis,P[a]-1):
      ans += 1
print(ans)
# Original solution
def find_root(x):
    if parent[x] == x:
        return x
    else:
        return find_root(parent[x])


# x,yの属する集合の併合
def unite(x, y):
    x = find_root(x)
    y = find_root(y)

    if x != y:
        if rank[x] < rank[y]:
            parent[x] = y
            size[y] += size[x]
        else:
            parent[y] = x
            size[x] += size[y]
            if rank[x] == rank[y]:
                rank[x] += 1


# xとyが同じグループかどうか判定
def same(x, y):
    return find_root(x) == find_root(y)


N, M = list(map(int, input().split()))
p = list(map(int, input().split()))
xy = [list(map(int, input().split())) for _ in range(M)]

parent = [i for i in range(N)]  # 根
rank = [1] * N  # 深さ
size = [1] * N  # iを根とするグループのサイズ

edge = [[xy[M - 1 - i][0] - 1, xy[M - 1 - i][1] - 1] for i in range(M)]
for i in range(M):
    a = find_root(edge[i][0])
    b = find_root(edge[i][1])
    unite(a, b)

ans = 0
for i in range(N):
    if p[i]-1 == i or same(p[i]-1, i):
        ans += 1
print(ans)

# Optimized solution
def find_root(x):
    if parent[x] == x:
        return x
    else:
        return find_root(parent[x])


# x,yの属する集合の併合
def unite(x, y):
    x = find_root(x)
    y = find_root(y)

    if x != y:
        if rank[x] < rank[y]:
            parent[x] = y
            size[y] += size[x]
        else:
            parent[y] = x
            size[x] += size[y]
            if rank[x] == rank[y]:
                rank[x] += 1


# xとyが同じグループかどうか判定
def same(x, y):
    return find_root(x) == find_root(y)


N, M = list(map(int, input().split()))
p = list(map(int, input().split()))
xy = [list(map(int, input().split())) for _ in range(M)]

parent = [i for i in range(N)]  # 根
rank = [1] * N  # 深さ
size = [1] * N  # iを根とするグループのサイズ

edge = [[xy[M - 1 - i][0] - 1, xy[M - 1 - i][1] - 1] for i in range(M)]
for i in range(M):
    unite(edge[i][0], edge[i][1])

ans = 0
for i in range(N):
    if p[i]-1 == i or same(p[i]-1, i):
        ans += 1
print(ans)
# Original solution
class UF():
  def __init__(self, N):
    self.parents = [ i for i in range(N+1) ]

  def union(self, f, t):
    p1 = self.find(f)
    p2 = self.find(t)
    if p1 == p2:
      return
    self.parents[p2] = p1

  def find(self, node):
    # if self.parents[node] == node:
    #   return node
    # else:
    #   return self.find(self.parents[node])
    while self.parents[node] != node:
      node = self.parents[node]
    return node

N,M = list(map(int, input().split()))
p = list(map(int, input().split()))
uf = UF(N)
xy = [ list(map(int, input().split())) for _ in range(M) ]

# for _ in range(M):
#   x,y = map(int, input().split())
#   uf.union(x,y)
for x,y in xy:
  uf.union(x,y)

res = 0
for i in range(N):
  if i+1 == p[i]:
    res += 1
  elif uf.parents[i+1] == uf.parents[p[i]]:
    res += 1
  elif uf.find(i+1) == uf.find(p[i]):
    res += 1

print(res)


# Optimized solution
class UF():
  def __init__(self, N):
    self.parents = [-1] * (N+1)

  def union(self, f, t):
    p1 = self.find(f)
    p2 = self.find(t)
    if p1 == p2:
      return

    if self.parents[p1] > self.parents[p2]:
      p1,p2 = p2,p1
    self.parents[p1] += self.parents[p2]
    self.parents[p2] = p1

  def find(self, node):
    if self.parents[node] < 0:
      return node
    else:
      self.parents[node] = self.find(self.parents[node])
      return self.parents[node]

  def same(self, n1, n2):
    return self.find(n1) == self.find(n2)

N,M = list(map(int, input().split()))
p = list(map(int, input().split()))
uf = UF(N)
xy = [ list(map(int, input().split())) for _ in range(M) ]

# for _ in range(M):
#   x,y = map(int, input().split())
#   uf.union(x,y)
for x,y in xy:
  uf.union(x,y)

res = 0
for i in range(N):
  if i+1 == p[i] or uf.same(i+1, p[i]):
    res += 1

print(res)
# Original solution
import sys
from collections import deque
readline = sys.stdin.readline
n, m = list(map(int, readline().split()))
P = list([int(x)-1 for x in readline().split()])
G = [set() for _ in range(n)]
for i in range(m):
    x, y = [int(x)-1 for x in readline().split()]
    G[x].add(y)
    G[y].add(x)

D = {}
cnt = 0
V = [-1]*n
while -1 in V:
    st = V.index(-1)
    que = deque([st])
    V[st] = cnt
    D[cnt] = set([P[st]])
    while que:
        nw = que.popleft()
        for nx in G[nw]:
            if V[nx] != -1: continue
            D[cnt].add(P[nx])
            V[nx] = cnt
            que.append(nx)
    cnt += 1
print((sum([int(i in D[V[i]]) for i in range(n)])))

# Optimized solution
import sys
readline = sys.stdin.readline

class UnionFind(object):
    def __init__(self, n):
        self._par = list(range(n))
        self.size = [1]*n

    def root(self, v):
        if self._par[v] == v:
            return v
        self._par[v] = self.root(self._par[v])
        return self._par[v]
    
    def unite(self, u, v):
        u, v = self.root(u), self.root(v)
        if u==v:
            return False
        if self.size[u] > self.size[v]:
            u, v = v, u
        self.size[v] += self.size[u]
        self._par[u] = v

    def is_connected(self, u, v):
        return self.root(u)==self.root(v)

n, m = list(map(int, readline().split()))
P = list([int(x)-1 for x in readline().split()])
uf = UnionFind(n)
for _ in range(m):
    x, y = [int(x)-1 for x in readline().split()]
    uf.unite(x,y)

ans = 0
for i in range(n):
    if uf.is_connected(i, P[i]):
        ans += 1
print(ans)
# Original solution
N,M = list(map(int,input().split()))
P = list(map(int,input().split()))
XY = [tuple(map(int,input().split())) for i in range(M)]

class UnionFind:
    def __init__(self,N):
        self.parent = [i for i in range(N)]
        self._size = [1] * N
        self.count = 0
    def root(self,a):
        if self.parent[a] == a:
            return a
        else:
            self.parent[a] = self.root(self.parent[a])
            return self.parent[a]
    def is_same(self,a,b):
        return self.root(a) == self.root(b)
    def unite(self,a,b):
        ra = self.root(a)
        rb = self.root(b)
        if ra == rb: return
        if self._size[ra] < self._size[rb]: ra,rb = rb,ra
        self._size[ra] += self._size[rb]
        self.parent[rb] = ra
        self.count += 1
    def size(self,a):
        return self._size[self.root(a)]

uf = UnionFind(N)
for x,y in XY:
    x,y = x-1,y-1
    if uf.is_same(x,y): continue
    uf.unite(x,y)
for i in range(N):
    uf.root(i)

from collections import defaultdict
ps = defaultdict(lambda: set())
ids = defaultdict(lambda: set())
rs = set()
for i,p in enumerate(P):
    r = uf.root(i)
    ps[r].add(p-1)
    ids[r].add(i)
    rs.add(r)

ans = 0
for r in rs:
    ans += len(ps[r] & ids[r])
print(ans)

# Optimized solution
N,M = list(map(int,input().split()))
P = list(map(int,input().split()))
XY = [tuple(map(int,input().split())) for i in range(M)]

class UnionFind:
    def __init__(self,N):
        self.parent = [i for i in range(N)]
        self._size = [1] * N
        self.count = 0
    def root(self,a):
        if self.parent[a] == a:
            return a
        else:
            self.parent[a] = self.root(self.parent[a])
            return self.parent[a]
    def is_same(self,a,b):
        return self.root(a) == self.root(b)
    def unite(self,a,b):
        ra = self.root(a)
        rb = self.root(b)
        if ra == rb: return
        if self._size[ra] < self._size[rb]: ra,rb = rb,ra
        self._size[ra] += self._size[rb]
        self.parent[rb] = ra
        self.count += 1
    def size(self,a):
        return self._size[self.root(a)]
uf = UnionFind(N)

for x,y in XY:
    x,y = x-1,y-1
    if not uf.is_same(x,y):
        uf.unite(x,y)
ans = 0
for i,p in enumerate(P):
    if uf.is_same(i,p-1):
        ans += 1
print(ans)
# Original solution
N, M=list(map(int, input().split()))

P=list(map(int, input().split()))
XY=[-1 for i in range(N)]


class UnionFind():
  # 作りたい要素数nで初期化
  # 使用するインスタンス変数の初期化
  def __init__(self, n):
    self.n = n
    # root[x]<0ならそのノードが根かつその値が木の要素数
    # rootノードでその木の要素数を記録する
    self.root = [-1]*(n+1)
    # 木をくっつける時にアンバランスにならないように調整する
    self.rnk = [0]*(n+1)

    # ノードxのrootノードを見つける
  def Find_Root(self, x):
    if(self.root[x] < 0):
      return x
    else:
    # ここで代入しておくことで、後の繰り返しを避ける
      self.root[x] = self.Find_Root(self.root[x])
      return self.root[x]

    # 木の併合、入力は併合したい各ノード
  def Unite(self, x, y):
    # 入力ノードのrootノードを見つける
    x = self.Find_Root(x)
    y = self.Find_Root(y)
    # すでに同じ木に属していた場合
    if (x == y):
      return 
      # 違う木に属していた場合rnkを見てくっつける方を決める
      #rnkが大きい方に結合
    elif (self.rnk[x] > self.rnk[y]):
      self.root[x] += self.root[y]
      self.root[y] = x

    else:
      self.root[y] += self.root[x]
      self.root[x] = y
      # rnkが同じ（深さに差がない場合）は1増やす
      if(self.rnk[x] == self.rnk[y]):
        self.rnk[y] += 1

  # xとyが同じグループに属するか判断
  def isSameGroup(self, x, y):
    return self.Find_Root(x) == self.Find_Root(y)

  # ノードxが属する木のサイズを返す
  def Count(self, x):
    return -self.root[self.Find_Root(x)]

uni=UnionFind(N)

for i in range(M):
  x, y=list(map(int, input().split()))
  XY[x-1]=y-1
  XY[y-1]=x-1
  uni.Unite(x-1, y-1)
  
cnt=0
for i in range(N):
  if not uni.isSameGroup(i,P[i]-1):
    cnt+=1
    
print((N-cnt))

# Optimized solution
class UnionFind():
  # 作りたい要素数nで初期化
  # 使用するインスタンス変数の初期化
  def __init__(self, n):
    self.n = n
    # root[x]<0ならそのノードが根かつその値が木の要素数
    # rootノードでその木の要素数を記録する
    self.root = [-1]*(n+1)
    # 木をくっつける時にアンバランスにならないように調整する
    self.rnk = [0]*(n+1)

    # ノードxのrootノードを見つける
  def Find_Root(self, x):
    if(self.root[x] < 0):
      return x
    else:
    # ここで代入しておくことで、後の繰り返しを避ける
      self.root[x] = self.Find_Root(self.root[x])
      return self.root[x]

    # 木の併合、入力は併合したい各ノード
  def Unite(self, x, y):
    # 入力ノードのrootノードを見つける
    x = self.Find_Root(x)
    y = self.Find_Root(y)
    # すでに同じ木に属していた場合
    if (x == y):
      return 
      # 違う木に属していた場合rnkを見てくっつける方を決める
      #rnkが大きい方に結合
    elif (self.rnk[x] > self.rnk[y]):
      self.root[x] += self.root[y]
      self.root[y] = x

    else:
      self.root[y] += self.root[x]
      self.root[x] = y
      # rnkが同じ（深さに差がない場合）は1増やす
      if(self.rnk[x] == self.rnk[y]):
        self.rnk[y] += 1

  # xとyが同じグループに属するか判断
  def isSameGroup(self, x, y):
    return self.Find_Root(x) == self.Find_Root(y)

  # ノードxが属する木のサイズを返す
  def Count(self, x):
    return -self.root[self.Find_Root(x)]


N,M=list(map(int, input().split()))
P=list(map(int, input().split()))
uni=UnionFind(N)
for i in range(M):
  x,y=list(map(int, input().split()))
  uni.Unite(x-1,y-1)
  
ans=0
for i,p in enumerate(P):
  if uni.isSameGroup(i,p-1):
    ans+=1
print(ans)
# Original solution
n,m = list(map(int,input().split()))
p = list(map(int, input().split()))
g = [[] for _ in range(0,n+1)]
visit = [None] * (n+1)
ans = 0
for _ in range(0,m):
    x,y = list(map(int, input().split()))
    g[x].append(y)
    g[y].append(x)
for i in range(1,n+1):
    if visit[i] is not None:
        continue
    stack = [i]
    part = [i]
    visit[i] = 0
    while len(stack) > 0:
        q = stack.pop()
        for qq in g[q]:
            if visit[qq] is None:
                visit[qq] = 0
                part.append(qq)
                stack.append(qq)
    part2 = []
    for i in part:
        part2.append(p[i-1])
    for i in part:
        if i in part2:
            ans += 1
print(ans)


# Optimized solution
n,m = list(map(int,input().split()))
p = list(map(int, input().split()))
g = [[] for _ in range(0,n+1)]
visit = [None] * (n+1)
ans = 0
for _ in range(0,m):
    x,y = list(map(int, input().split()))
    g[x].append(y)
    g[y].append(x)
for i in range(1,n+1):
    if visit[i] is not None:
        continue
    stack = [i]
    part = [i]
    visit[i] = 0
    while len(stack) > 0:
        q = stack.pop()
        for qq in g[q]:
            if visit[qq] is None:
                visit[qq] = 0
                part.append(qq)
                stack.append(qq)
    part2 = []
    for i in part:
        part2.append(p[i-1])
    ans += len(list(set(part) & set(part2)))
print(ans)
# Original solution
N, M = list(map(int, input().split()))
P = list(map(int, input().split()))

parent = list(range(N))
rank = [0] * N
key = [[i] for i in range(N)]
val = [[p-1] for p in P]

# union-find
def getRoot(x):
  p = parent[x]
  if p == x:
    return p
  ret = getRoot(p)
  parent[x] = ret
  return ret

def join(x, y):
  rx = getRoot(x)
  ry = getRoot(y)
  if rank[rx] < rank[ry]:
    parent[rx] = ry
    key[ry] += key[rx]
    val[ry] += val[rx]
  else:
    parent[ry] = rx
    key[rx] += key[ry]
    val[rx] += val[ry]
    if rank[rx] == rank[ry]:
      rank[rx] += 1

for _ in range(M):
  x, y = list(map(int, input().split()))
  join(x-1, y-1)

ans = 0
for i in range(N):
  if parent[i] == i:
    ans += len(set(key[i]).intersection(set(val[i])))

print(ans)

# Optimized solution
N, M = list(map(int, input().split()))
P = list(map(int, input().split()))

parent = list(range(N))
rank = [0] * N

idx = [None] * N
for i in range(N):
  idx[P[i]-1] = i

# union-find
def getRoot(x):
  p = parent[x]
  if p == x:
    return p
  ret = getRoot(p)
  parent[x] = ret
  return ret

def join(x, y):
  rx = getRoot(x)
  ry = getRoot(y)
  if rank[rx] < rank[ry]:
    parent[rx] = ry
  else:
    parent[ry] = rx
    if rank[rx] == rank[ry]:
      rank[rx] += 1

for _ in range(M):
  x, y = list(map(int, input().split()))
  join(x-1, y-1)

ans = 0
for i in range(N):
  if getRoot(i) == getRoot(idx[i]):
    ans += 1

print(ans)
# Original solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.root = [-1]*(n+1)
        self.rnk = [0]*(n+1)

    def find_root(self, x):
        if(self.root[x] < 0):
            return x
        else:
            self.root[x] = self.find_root(self.root[x])
            return self.root[x]
        
    def unite(self, x, y):
        x = self.find_root(x)
        y = self.find_root(y)
        if(x == y):
            return 
        elif(self.rnk[x] > self.rnk[y]):
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            if(self.rnk[x] == self.rnk[y]):
                self.rnk[y] += 1
                
    def is_same_group(self, x, y):
        return self.find_root(x) == self.find_root(y)

    def count(self, x):
        return -self.root[self.find_root(x)]

    
f=lambda:list(map(int,input().split()))
N,M=f()
uf=UnionFind(N+1)

p=list(f())
for _ in [0]*M:
    uf.unite(*f())

index_list=[[] for _ in [0]*(N+1)]
group_list=[[] for _ in [0]*(N+1)]
for i in range(1,N+1):
    index_list[uf.find_root(i)].append(p.index(i)+1)
    group_list[uf.find_root(i)].append(i)
    
res=0
for i in range(1,N+1):
    if i==uf.find_root(i):
        res+=len(set(index_list[i]) & set(group_list[i]))

print(res)

# Optimized solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.root = [-1]*(n+1)
        self.rnk = [0]*(n+1)

    def find_root(self, x):
        if(self.root[x] < 0):
            return x
        else:
            self.root[x] = self.find_root(self.root[x])
            return self.root[x]
        
    def unite(self, x, y):
        x = self.find_root(x)
        y = self.find_root(y)
        if(x == y):
            return 
        elif(self.rnk[x] > self.rnk[y]):
            self.root[x] += self.root[y]
            self.root[y] = x
        else:
            self.root[y] += self.root[x]
            self.root[x] = y
            if(self.rnk[x] == self.rnk[y]):
                self.rnk[y] += 1
                
    def is_same_group(self, x, y):
        return self.find_root(x) == self.find_root(y)

    def count(self, x):
        return -self.root[self.find_root(x)]

    
f=lambda:list(map(int,input().split()))
N,M=f()
uf=UnionFind(N+1)

p=list(f())
p_index=[[] for _ in [0]*(N+1)]
for i in range(1,N+1):
    p_index[p[i-1]]=i
for _ in [0]*M:
    uf.unite(*f())


index_list=[[] for _ in [0]*(N+1)]
group_list=[[] for _ in [0]*(N+1)]

for i in range(1,N+1):
    index_list[uf.find_root(i)].append(p_index[i])
    group_list[uf.find_root(i)].append(i)
    
res=0
for i in range(1,N+1):
    if i==uf.find_root(i):
        res+=len(set(index_list[i]) & set(group_list[i]))

print(res)
# Original solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.root = [-1]*(n+1)
        self.rnk = [0]*(n+1)
 
    def Find_Root(self, x):
        if(self.root[x] < 0):
            return x
        else:
            self.root[x] = self.Find_Root(self.root[x])
            return self.root[x]

    def Unite(self, x, y):
        x = self.Find_Root(x)
        y = self.Find_Root(y)
 
        if(x == y):
            return 
        elif(self.rnk[x] > self.rnk[y]):
            self.root[y] = x
        else:
            self.root[x] = y
            if(self.rnk[x] == self.rnk[y]):
                self.rnk[y] += 1
 
    def isSameGroup(self, x, y):
        return self.Find_Root(x) == self.Find_Root(y)
 
    def Count(self, x):
        return -self.root[self.Find_Root(x)]

def main():
  N, M = list(map(int, input().split()))
  p = list(map(int, input().split()))
  xy = [list(map(int, input().split())) for _ in range(M)]
  U = UnionFind(N+1)
  for x, y in xy:
    U.Unite(x, y)
  root_dict = {}
  for i in range(1, N+1):
    root = U.Find_Root(i)
    if root in root_dict:
      root_dict[root] += [i]
    else:
      root_dict[root] = [i]
  ans = 0
  for i in range(1, N+1):
    root = U.Find_Root(i)
    if p[i-1] in root_dict[root]:
      ans += 1
  print(ans)


if __name__ == "__main__":
  main()

# Optimized solution
class UnionFind():
    def __init__(self, n):
        self.n = n
        self.root = [-1]*(n+1)
        self.rnk = [0]*(n+1)
 
    def Find_Root(self, x):
        if(self.root[x] < 0):
            return x
        else:
            self.root[x] = self.Find_Root(self.root[x])
            return self.root[x]

    def Unite(self, x, y):
        x = self.Find_Root(x)
        y = self.Find_Root(y)
 
        if(x == y):
            return 
        elif(self.rnk[x] > self.rnk[y]):
            self.root[y] = x
        else:
            self.root[x] = y
            if(self.rnk[x] == self.rnk[y]):
                self.rnk[y] += 1
 
    def isSameGroup(self, x, y):
        return self.Find_Root(x) == self.Find_Root(y)
 
    def Count(self, x):
        return -self.root[self.Find_Root(x)]

def main():
  N, M = list(map(int, input().split()))
  p = list(map(int, input().split()))
  xy = [list(map(int, input().split())) for _ in range(M)]
  U = UnionFind(N+1)
  for x, y in xy:
    U.Unite(x, y)
  ans = 0
  for i in range(1, N+1):
    if U.isSameGroup(i, p[i-1]):
      ans += 1
  print(ans)


if __name__ == "__main__":
  main()
# Original solution
# coding: utf-8
# Your code here!

class UnionFind():
    #負の値はルートで集合の個数
    #正の値は次の要素を返す
    def __init__(self,size):
        self.table = [-1 for _  in range(size)]

    #集合の代表を求める
    def find(self,x):
        while self.table[x] >= 0:
            #根に来た時,self.table[根のindex]は負の値なのでx = 根のindexで値が返される。
            x = self.table[x]
        return x

    #併合
    def union(self,x,y):
        s1 = self.find(x)#根のindex,table[s1]がグラフの高さ
        s2 = self.find(y)
        if s1 != s2:#根が異なる場合
            if self.table[s1] != self.table[s2]:#グラフの高さが異なる場合
                if self.table[s1] < self.table[s2]:
                    self.table[s2] = s1
                else:
                    self.table[s1] = s2
            else:
                #グラフの長さが同じ場合,どちらを根にしても変わらない
                #その際,グラフが1長くなることを考慮する
                self.table[s1] += -1
                self.table[s2] = s1
        return
    
n,m = list(map(int,input().split()))
pp = list(map(int,input().split()))
p = [i-1 for i in pp]


uf = UnionFind(n)
for i in range(m):
    x,y = list(map(int,input().split()))
    uf.union(x-1,y-1);
c=0;
for i in range(n):
    if(uf.find(i)==uf.find(p[i])):
        c = c+1

print(c)
    
    
    
    

# Optimized solution
class UnionFind:
    def __init__(self, size):
        self.parent = [-1 for i in range(size)]#非負なら親ノード，負ならグループの要素数

    def root(self, x):
        if self.parent[x] < 0:
            return x
        else:
            self.parent[x] = self.root(self.parent[x]) #xをxの根に直接つなぐ
            return self.parent[x]

    def union(self, x, y):
        x = self.root(x)
        y = self.root(y)
        if x == y:
            return False

        if self.parent[x] > self.parent[y]: #xの要素数がyの要素数より「小さい」とき
            x,y=y,x
        self.parent[x] += self.parent[y] #xの要素数を更新
        self.parent[y] = x #yをxにつなぐ
        return True

    def same(self, x, y):
        return self.root(x) == self.root(y)



n,m = list(map(int,input().split()))
p = list([int(x)-1 for x in input().split()])

uf = UnionFind(n)
for i in range(m):
    x,y=list(map(int,input().split()))
    uf.union(x-1,y-1)

ans = 0
for i in range(n):
    if uf.same(i,p[i]):
        ans += 1

print(ans)