Problem:


Score : 400 points

Problem Statement

We have a permutation of the integers from 1 through N, p_1, p_2, .., p_N.
We also have M pairs of two integers between 1 and N (inclusive), represented as (x_1,y_1), (x_2,y_2), .., (x_M,y_M).
AtCoDeer the deer is going to perform the following operation on p as many times as desired so that the number of i (1 ≤ i ≤ N) such that p_i = i is maximized:

Choose j such that 1 ≤ j ≤ M, and swap p_{x_j} and p_{y_j}.

Find the maximum possible number of i such that p_i = i after operations.

Constraints

2 ≤ N ≤ 10^5

1 ≤ M ≤ 10^5

p is a permutation of integers from 1 through N.

1 ≤ x_j,y_j ≤ N

x_j ≠ y_j

If i ≠ j, \{x_i,y_i\} ≠ \{x_j,y_j\}.

All values in input are integers.

Input

Input is given from Standard Input in the following format:

N M
p_1 p_2 .. p_N
x_1 y_1
x_2 y_2
:
x_M y_M

Output

Print the maximum possible number of i such that p_i = i after operations.

Sample Input 1

5 2
5 3 1 4 2
1 3
5 4

Sample Output 1

2

If we perform the operation by choosing j=1, p becomes 1 3 5 4 2, which is optimal, so the answer is 2.

Sample Input 2

3 2
3 2 1
1 2
2 3

Sample Output 2

3

If we perform the operation by, for example, choosing j=1, j=2, j=1 in this order, p becomes 1 2 3, which is obviously optimal.
Note that we may choose the same j any number of times.

Sample Input 3

10 8
5 3 6 8 7 10 9 1 2 4
3 1
4 1
5 9
2 5
6 5
3 5
8 9
7 9

Sample Output 3

8

Sample Input 4

5 1
1 2 3 4 5
1 5

Sample Output 4

5

We do not have to perform the operation. 



Sample input:
5 2
5 3 1 4 2
1 3
5 4


Sample output:
2

Submission status: Accepted

Solution code:
from collections import deque

n, m = map(int, input().split())
plst = list(map(int, input().split()))
edges = [[] for _ in range(n)]
for _ in range(m):
    x, y = map(int, input().split())
    x -= 1
    y -= 1
    edges[x].append(y)
    edges[y].append(x)
    
check = [False for _ in range(n)]
uni = [0 for _ in range(n)]
pos = 0
queue = deque()
for i in range(n):
    if check[i]:
        continue
    pos += 1
    queue.append(i)
    check[i] = True
    while queue:
        now = queue.popleft()
        uni[now] += pos
        uni[plst[now] - 1] -= pos
        for aft in edges[now]:
            if check[aft]:
                continue
            check[aft] = True
            queue.append(aft)
print(uni.count(0))

Submission status: Accepted

Solution code:
n,m=map(int,input().split())
P=[int(i) for i in input().split()]

uf=[i for i in range(n+1)]
def ufuf(x):
    while x!=uf[x]:
        x=uf[x]
    return x

for i in range(m):
    x,y=map(int,input().split())
    if ufuf(x)<ufuf(y):
        uf[ufuf(y)]=ufuf(x)
    else:
        uf[ufuf(x)]=ufuf(y)

ans=0
for i in range(1,n+1):
    if ufuf(i)==ufuf(P[i-1]):
        ans+=1
print(ans)

Submission status: Accepted

Solution code:
import sys, re
from collections import deque, defaultdict, Counter
from math import ceil, sqrt, hypot, factorial, pi, sin, cos, tan, asin, acos, atan, radians, degrees, log2, gcd
from itertools import accumulate, permutations, combinations, combinations_with_replacement, product, groupby
from operator import itemgetter, mul
from copy import deepcopy
from string import ascii_lowercase, ascii_uppercase, digits
from bisect import bisect, bisect_left, insort, insort_left
from heapq import heappush, heappop
from functools import reduce
def input(): return sys.stdin.readline().strip()
def INT(): return int(input())
def MAP(): return map(int, input().split())
def LIST(): return list(map(int, input().split()))
def ZIP(n): return zip(*(MAP() for _ in range(n)))
sys.setrecursionlimit(10 ** 9)
INF = float('inf')
mod = 10 ** 9 + 7 
#mod = 998244353
from decimal import *
#import numpy as np
#decimal.getcontext().prec = 10

class UnionFind():
    def __init__(self, n):
        self.n = n
        # parents[i]: 要素iの親要素の番号
        # 要素iが根の場合、parents[i] = -(そのグループの要素数)
        self.parents = [-1] * n
    def find(self, x):
        if 0 > self.parents[x]:
            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
    # 要素xが属するグループの要素数を返す
    def size(self, x):
        return -self.parents[self.find(x)]
    def same(self, x, y):
        return self.find(x) == self.find(y)
    # 要素xが属するグループに属する要素をリストで返す
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
    # 全ての根の要素をリストで返す
    def roots(self):
        return [i for i, x in enumerate(self.parents) if 0 > x]
    # グループの数を返す
    def group_count(self):
        return len(self.roots())
    # 辞書{根の要素: [そのグループに含まれる要素のリスト], ...}を返す
    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}
    # print()での表示用
    # all_group_members()をprintする
    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

N, M = MAP()
p = LIST()

tree = UnionFind(N)

for _ in range(M):
	x, y = MAP()
	tree.union(x-1, y-1)

ans = 0
for i, x in enumerate(p):
	if tree.same(i, x-1):
		ans += 1

print(ans)

Submission status: Accepted

Solution code:
class UnionFind(object):
    def __init__(self, n=1):
        self.par = [i for i in range(n)]
        self.rank = [0 for _ in range(n)]

    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                x, y = y, x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.par[y] = x

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

n,m=map(int, input().split())
uf1=UnionFind(n)
p=list(map(int,input().split()))
for i in range(m):
    a,b=map(int, input().split())
    uf1.union(a-1,b-1)

for i in range(n):uf1.find(i) 
ans=0
for i in range(n):
    if uf1.par[i]==uf1.par[p[i]-1]:
        ans+=1
print(ans)

Submission status: Accepted

Solution code:
n,m=list(map(int,input().split()))
p=list(map(int,input().split()))
from collections import defaultdict
from collections import deque
c=0
for i in range(n):
    if p[i]==i+1:
        c+=1
vis=[0]*n
def dfs(x):
    vis[x]=1
    a.add(x+1)
    
    di = deque()
    di.append(x)
    while di:
        now = di.popleft()
        for j in d[now]:
            if not vis[j]:
                vis[j] = 1
                a.add(j+1)
                di.append(j)
    
    
    for u in d[x]:
        if vis[u]==0:
            dfs(u)
d=defaultdict(list)
for i in range(m):
    a,b=list(map(int,input().split()))
    d[a-1].append(b-1)
    d[b-1].append(a-1)
ans=0
for i in range(n):
    if vis[i]==0:
        a=set()
        dfs(i)
        l=0
        z=0
        for j in a:
            if p[j-1] in a:
                z+=1
            if p[j-1]==j:
                l+=1
        ans=max(ans,c+z-l)
print(ans)

Submission status: Accepted

Solution code:
#Union Find
class union_find:
    #初期化
    #根なら-size,子なら親の頂点
    # par = [-1]*N
    def __init__(self, N):
        self.par = [-1]*N

    #xの根を求める
    def find(self, x):
        if self.par[x] < 0:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    #xとyの属する集合の併合
    def unite(self, x,y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return False
        else:
            #sizeの大きいほうがx
            if self.par[x] > self.par[y]:
                x,y = y,x
            self.par[x] += self.par[y]
            self.par[y] = x
            return True

    #xとyが同じ集合に属するかの判定
    def same(self, x,y):
        return self.find(x) == self.find(y)

    #xが属する集合の個数
    def size(self, x):
        return -self.par[self.find(x)]

N, M = map(int, input().split())
P = list(map(lambda x:int(x)-1, input().split()))
uf = union_find(N)
for _ in range(M):
    x, y = map(int, input().split())
    x -= 1
    y -= 1
    uf.unite(x, y) 

ans = 0
for i in range(N):
    if uf.same(P[i], i):
        ans += 1
print(ans)

Submission status: Accepted

Solution code:
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 size(self, x):
    return -self.parents[self.find(x)]

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

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

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

  def group_count(self):
    return len(self.roots())

  def all_group_members(self):
    return {r: self.members(r) for r in self.roots()}

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

N, M = map(int, input().split())
Ps = [0] + list(map(int, input().split()))

uf = UnionFind(N+1)

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

rlt = 0
for i in range(1,N+1):
  if uf.find(i) == uf.find(Ps[i]):
    rlt += 1
  
print(rlt)

Submission status: Accepted

Solution code:
"""
初期状態でpiにいる数が、pi番目にたどり着けるかを判定すればよい。
unionFind木が使える
"""
N,M = map(int,input().split())
P = list(map(int,input().split()))
class UnionFind():
    def __init__(self,n):
        self.n=n
        self.parents = [i for i in range(n+1)]
        self.size = [1]*(n+1)
    def find(self,x):
        if self.parents[x]==x:
            return x
        else:
            self.parents[x]=self.find(self.parents[x])
            return self.parents[x]
    def unite(self,x,y):
        xRoot = self.find(x)
        yRoot = self.find(y)
        if xRoot == yRoot:
            return
        if self.size[xRoot]>self.size[yRoot]:
            self.parents[yRoot] = xRoot
            self.size[xRoot] += self.size[yRoot]
            self.size[yRoot] = 0
        else:
            self.parents[xRoot] = yRoot
            self.size[yRoot] += self.size[xRoot]
            self.size[xRoot] = 0
tree = UnionFind(N)
for _ in range(M):
    x,y = map(int,input().split())
    tree.unite(x,y)
cnt = 0
for i in range(N):
    p = P[i]
    if tree.find(i+1)==tree.find(p):
        cnt += 1

print(cnt)

Submission status: Accepted

Solution code:
N, M = map(int,input().split())
p = list(map(int,input().split()))

def find(x, U):
  if x == U[x]:
    return x
  else:
    k = find(U[x], U)
    U[x] = k
    return k

U = [i for i in range(N)]
for _ in range(M):
  x, y = map(int,input().split())
  x -= 1
  y -= 1
  a = find(p[x]-1, U)
  b = find(p[y]-1, U)
  U[a] = b
  
for i in range(N):
  find(i, U)

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

Submission status: Accepted

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

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 # size[u] <= size[v]
        self.size[v] += self.size[u]
        self._par[u] = v
 
    def is_connected(self, u, v):
        return self.root(u) == self.root(v)

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

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

print(ans)

Submission status: Accepted

Solution code:
#!/usr/bin/env python3
import sys


def solve(N: int, M: int, p: "List[int]", x: "List[int]", y: "List[int]"):
    uf = UnionFind(N)
    for xx, yy in zip(x, y):
        uf.union(xx-1, yy-1)
    ans = 0
    for members in uf.all_group_members().values():
        s = {p[m]-1 for m in members}
        ans += sum(m in s for m in members)
    return ans

# https://note.nkmk.me/python-union-find/
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 size(self, x):
        return -self.parents[self.find(x)]
        
    def same(self, x, y):
        return self.find(x) == self.find(y)
    
    def members(self, x):
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i) == root]
    
    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]
 
    def group_count(self):
        return len(self.roots())
    
    def all_group_members(self):
        res = {}
        for i in range(self.n):
            r = self.find(i)
            if r not in res:
                res[r] = []
            res[r].append(i)
        return res
    
    def __str__(self):
        return '\n'.join('{}: {}'.format(r, self.members(r)) for r in self.roots())

# Generated by 1.1.7.1 https://github.com/kyuridenamida/atcoder-tools
def main():
    def iterate_tokens():
        for line in sys.stdin:
            for word in line.split():
                yield word
    tokens = iterate_tokens()
    N = int(next(tokens))  # type: int
    M = int(next(tokens))  # type: int
    p = [int(next(tokens)) for _ in range(N)]  # type: "List[int]"
    x = [int()] * (M)  # type: "List[int]"
    y = [int()] * (M)  # type: "List[int]"
    for i in range(M):
        x[i] = int(next(tokens))
        y[i] = int(next(tokens))
    print(solve(N, M, p, x, y))

def test():
    import doctest
    doctest.testmod()

if __name__ == '__main__':
    #test()
    main()

Submission status: Accepted

Solution code:
import sys
readline = sys.stdin.readline

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 size(self, x):
        return -self.parents[self.find(x)]

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

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

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

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

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

def main():
    N, M = map(int, readline().rstrip().split())
    P = list(map(int, readline().rstrip().split()))
    uf = UnionFind(N)
    for _ in range(M):
        x, y = map(int, readline().rstrip().split())
        uf.union(x-1, y-1)
    
    res = 0
    for i, p in enumerate(P):
        if uf.same(p - 1, i):
            res += 1
    
    print(res)

if __name__ == '__main__':
    main()

Submission status: Accepted

Solution code:
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 size(self, x):
        return -self.parents[self.find(x)]

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

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

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

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

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

n, m = [int(i) for i in input().split()]
uf = UnionFind(n)

p = [int(i) for i in input().split()]
for i in range(m):
    x, y = [int(j) for j in input().split()]
    uf.union(x-1, y-1)

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

print(count)

Submission status: Accepted

Solution code:
#import sys
#import numpy as np
import math
#import itertools
#from fractions import Fraction
#import itertools
from collections import deque
from collections import Counter
#import heapq
#from fractions  import gcd
#input=sys.stdin.readline
#import bisect
class UnionFind(): #集合の取り扱い、辺に重み、階層を意識するときは使えない
    def __init__(self,n):
        self.n=n
        self.par = [-1]*n
    def find(self,x):
        if self.par[x] <0: #負なら親、要素数
            return x
        else: #正なら親の番号
            self.par[x]=self.find(self.par[x]) #一回調べたら根を繋ぎ変える、経路圧縮
            return self.par[x]
    def union(self,x,y):
        x=self.find(x) #x,yの親
        y=self.find(y)

        if x==y: #親が同じなら何もしない
            return 
        if self.par[x]>self.par[y]: #要素数が大きい方をxにする
            x,y=y,x
        self.par[x]+=self.par[y] #xがある集合にyが入った集合をくっつける
        self.par[y]=x

    def size(self,x):
        return -self.par[self.find(x)] #親のサイズ
    
    def same(self,x,y): #親が同じ、すなわち同じ集合か
        return self.find(x)==self.find(y)

    def member(self,x): #同じ木（集合）にある要素全部リストで返す
        root = self.find(x)
        return [i for i in range(self.n) if self.find(i)==root ]
    
    def roots(self): #集合の根になってる要素をリストで返す
        return [i for i , x in enumerate(self.par) if x<0]
    
    def group_count(self): #集合の数、木の数
        return len(self.roots())

    def all_member(self): #辞書型で返す
        return {r: self.member(r) for r in self.roots()}
    def __str__(self):
        return "\n".join('{}:{}'.format(r,self.member(r)) for r in self.roots)

n,m=map(int,input().split())
p=list(map(int,input().split()))
tree=UnionFind(n)
for i in range(m):
  x,y=map(int,input().split())
  tree.union(x-1,y-1)
ans=0
for i in range(n):
  if tree.same(p[i]-1,i):
    ans+=1
print(ans)

Submission status: Accepted

Solution code:
class UnionFind(object):
    def __init__(self, n=1):
        self.par = [i for i in range(n)]
        self.rank = [0 for _ in range(n)]
        self.size = [1 for _ in range(n)]
    def find(self, x):
        """
        x が属するグループを探索
        """
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]
    def union(self, x, y):
        """
        x と y のグループを結合
        """
        x = self.find(x)
        y = self.find(y)
        if x != y:
            if self.rank[x] < self.rank[y]:
                x, y = y, x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1
            self.par[y] = x
            self.size[x] += self.size[y]
    def is_same(self, x, y):
        """
        x と y が同じグループか否か
        """
        return self.find(x) == self.find(y)
    def get_size(self, x):
        """
        x が属するグループの要素数
        """
        x = self.find(x)
        return self.size[x]

    
N,M = map(int,input().split())
P = list(map(int,input().split()))
uf = UnionFind(N)
for i in range(M):
    x,y = map(int,input().split())
    x-=1;y-=1
    uf.union(x,y)
ans = 0
for i in range(N):
    now = P[i]-1
    loc = i
    if uf.is_same(now,loc):
        ans += 1
print(ans)

Submission status: Accepted

Solution code:
import sys
from collections import defaultdict, deque
import math

# import copy
from bisect import bisect_left, bisect_right
# import heapq

# sys.setrecursionlimit(1000000)

# input aliases
input = sys.stdin.readline

getS = lambda: input().strip()
getN = lambda: int(input())
getList = lambda: list(map(int, input().split()))
getZList = lambda: [int(x) - 1 for x in input().split()]

INF = 10 ** 20
MOD = 10**9 + 7
divide = lambda x: pow(x, MOD-2, MOD)

class UnionFind:
    def __init__(self, n):
        self.par = [i for i in range(n+1)]
        self.rank = [0] * (n+1)

    # 検索
    def find(self, x):
        if self.par[x] == x:
            return x
        else:
            self.par[x] = self.find(self.par[x])
            return self.par[x]

    # 併合
    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)
        if self.rank[x] < self.rank[y]:
            self.par[x] = y
        else:
            self.par[y] = x
            if self.rank[x] == self.rank[y]:
                self.rank[x] += 1

    # 同じ集合に属するか判定
    def same_check(self, x, y):
        return self.find(x) == self.find(y)

    def update(self):
        for i in range(len(self.par)):
            self.par[i] = self.find(i)

def solve():
    n,m = getList()
    nums = getList()
    uf = UnionFind(n)
    for i in range(m):
        x, y = getList()
        uf.union(x, y)
    uf.update()
    st1 = [[] for i in range(n + 1)]
    st2 = [[] for i in range(n + 1)]
    for i in range(n):
        st1[uf.par[i+1]].append(i+1)
        st2[uf.par[i+1]].append(nums[i])

    ans = 0
    for s1, s2 in zip(st1, st2):
        ans += len(list(set(s1) & set(s2)))



    print(ans)
def main():
    n = getN()
    for _ in range(n):
        solve()


if __name__ == "__main__":
    # main()
    solve()

Submission status: Accepted

Solution code:
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 size(self, x):
        return -self.parents[self.find(x)]

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

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

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

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

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

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

Submission status: Accepted

Solution code:
# D - Equals
# https://atcoder.jp/contests/arc097/tasks/arc097_b

class UnionFind():
    # https://www.slideshare.net/chokudai/union-find-49066733
    # 作りたい要素数nで初期化
    # 使用するインスタンス変数の初期化
    def __init__(self, n):
        self.n = n
        # root[x]<0ならそのノードが根かつその値が木の要素数
        # rootノードでその木の要素数を記録する
        # root[x]>=0の場合は、特に直接的な意味を持たない気がする。計算に寄与するので意味はあるのだろうが。
        self.root = [-1]*(n+1)
        # 木をくっつける時にアンバランスにならないように調整する
        # 無結合の時はRank=0,結合して1つ木が深くなると根がRank+=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を見てくっつける方を決める
        # (1)xのランクの方が大きい(位置が深い)場合
        elif(self.rnk[x] > self.rnk[y]):
            self.root[x] += self.root[y]
            self.root[y] = x

        # (2)yのランクの方が大きい(位置が深い)場合 or 等しい場合
        # また等しい場合、引数の2つめのyの方のランクを1つ増やす
        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が同じグループに属するか判断
    # Return: True or False
    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=map(int,input().split())
p=list(map(int,input().split()))
x=[0]*M
y=[0]*M
UN=UnionFind(N)
for i in range(M):
    x[i],y[i]= map(int,input().split())
    UN.Unite(x[i],y[i])
    
cnt=0
for i in range(N):
    #print(p[i],i+1,UN.root[i])
    if UN.isSameGroup(i+1,p[i]):
        #print(i+1)
        cnt +=1
print(cnt)

Submission status: Accepted

Solution code:
import sys


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


sys.setrecursionlimit(20000000)

MOD = 10 ** 9 + 7
INF = float("inf")


class UnionFind:
    # 初期化
    def __init__(self, n_nodes):
        self.n_nodes = n_nodes
        # 親要素のノード番号を格納する.初めは全て親ノード.
        # self.parent[x] == x の時，そのノードは根.
        self.parent = [i for i in range(n_nodes)]
        # 木の高さを格納する．
        self.rank = [1] * n_nodes
        # 自分と同じグループの要素数を格納．親要素を参照するようにする
        self.size = [1] * n_nodes

    # 検索. 根を返す.
    def find(self, x):
        if self.parent[x] == x:
            return x
        else:
            # 一度調べた値は，根に繋ぎ直す.
            self.parent[x] = self.find(self.parent[x])
            return self.parent[x]

    # 併合.
    # 木の高さが低くなるように.
    # 親要素の書き換えは少ない方がいい
    def unite(self, x, y):
        # 根を探す．
        x = self.find(x)
        y = self.find(y)
        # 根が同じ場合はそのまま
        if x == y:
            return
        # 木の高さを比較し，低い方を高い方の根に貼る.
        if self.rank[x] > self.rank[y]:
            self.parent[y] = x
            self.size[x] += self.size[y]
        else:
            self.parent[x] = y
            self.size[y] += self.size[x]
            if self.rank[x] == self.rank[y]:
                self.rank[y] += 1

    # 同じ集合に属するか判定
    def check(self, x, y):
        return self.find(x) == self.find(y)

    # 同一グループに所属する要素数を返す
    def get_size(self, x):
        return self.size[self.find(x)]

    # 親要素のリストを返す
    def get_parent_list(self):
        return [i for i in range(self.n_nodes) if self.find(i) == i]

    # グループの数を返す
    def get_n_groups(self):
        return len(self.get_parent_list())

    # 同じグループに所属する要素の集合を返す
    def get_members(self, x):
        parent = self.find(x)
        return [i for i in range(self.n_nodes) if self.find(i) == parent]

    # 全ての根に対する，グループのメンバーを返す．
    def get_members_dict(self):
        return {par: self.get_members(par) for par in self.get_parent_list()}


def main():
    N, M = map(int, input().split())
    P = list(map(int, input().split()))
    UF = UnionFind(N)
    for _ in range(M):
        x, y = map(int, input().split())
        UF.unite(x - 1, y - 1)

    cnt = 0
    for i in range(N):
        a = P[i] - 1
        if UF.check(i, a):
            cnt += 1
    print(cnt)


if __name__ == "__main__":
    main()

Submission status: Accepted

Solution code:
##### UnionFind #####
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 size(self, x):
        return -self.parents[self.find(x)]

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

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

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

    def group_count(self):
        return len(self.roots())

    def all_group_members(self):
        return {r: self.members(r) for r in self.roots()}

(n, m), p, *q = [list(map(int, o.split())) for o in open(0)]
UF = UnionFind(-~n)
for a, b in q:UF.union(a, b)
print(sum(UF.same(p[i], -~i) for i in range(n)))

Submission status: Accepted

Solution code:
n,m=map(int,input().split())
p=list(map(int,input().split()))
class UnionFind:
    #def   -> foo=UnionFind(n,1)  <- 1-based index, default is 0
    #method -> foo.hoge(huga)
    __slots__ = ["_size", "_first_idx", "_parents"]
    def __init__(self, size: int, first_index: int = 0) -> None:
        self._size = size
        self._first_idx = first_index
        self._parents = [-1] * (size + first_index)
    def find(self, x: int) -> int:
        if self._parents[x] < 0:
            return x
        self._parents[x] = self.find(self._parents[x])
        return self._parents[x]
    def same(self, x: int, y: int) -> bool:
        return self.find(x) == self.find(y)
    def unite(self, x: int, y: int) -> bool:
        x, y = self.find(x), self.find(y)
        if x == y:
            return False
        if self._parents[x] > self._parents[y]:
            x, y = y, x
        self._parents[x] += self._parents[y]
        self._parents[y] = x
        return True
    def size(self, x: int) -> int:
        return -self._parents[self.find(x)]
    def group_count(self) ->int:
        return sum(1 for i in  self._parents if i<0)-self._first_idx
    def connected(self) ->bool:
        return self._parents[self.find(self._first_idx)]==-self._size
uf=UnionFind(n,1)

for i in range(m):
    uf.unite(*map(int,input().split()))
print(len(tuple(1 for i in range(n) if uf.same(i+1,p[i]))))