Peaceful Rooks (Codeforces 1465C)
Tags: Greedy, dsu

You are given a $$$n \times n$$$ chessboard. Rows and columns of the board are numbered from $$$1$$$ to $$$n$$$. Cell $$$(x, y)$$$ lies on the intersection of column number $$$x$$$ and row number $$$y$$$.Rook is a chess piece, that can in one turn move any number of cells vertically or horizontally. There are $$$m$$$ rooks ($$$m < n$$$) placed on the chessboard in such a way that no pair of rooks attack each other. I.e. there are no pair of rooks that share a row or a column.In one turn you can move one of the rooks any number of cells vertically or horizontally. Additionally, it shouldn't be attacked by any other rook after movement. What is the minimum number of moves required to place all the rooks on the main diagonal?The main diagonal of the chessboard is all the cells $$$(i, i)$$$, where $$$1 \le i \le n$$$.

Input: The first line contains the number of test cases $$$t$$$ ($$$1 \leq t \leq 10^3$$$). Description of the $$$t$$$ test cases follows.The first line of each test case contains two integers $$$n$$$ and $$$m$$$ — size of the chessboard and the number of rooks ($$$2 \leq n \leq 10^5$$$, $$$1 \leq m < n$$$). Each of the next $$$m$$$ lines contains two integers $$$x_i$$$ and $$$y_i$$$ — positions of rooks, $$$i$$$-th rook is placed in the cell $$$(x_i, y_i)$$$ ($$$1 \leq x_i, y_i \leq n$$$). It's guaranteed that no two rooks attack each other in the initial placement.The sum of $$$n$$$ over all test cases does not exceed $$$10^5$$$.

Output: For each of $$$t$$$ test cases print a single integer — the minimum number of moves required to place all the rooks on the main diagonal.It can be proved that this is always possible.

Note: Possible moves for the first three test cases:  $$$(2, 3) \to (2, 2)$$$  $$$(2, 1) \to (2, 3)$$$, $$$(1, 2) \to (1, 1)$$$, $$$(2, 3) \to (2, 2)$$$  $$$(2, 3) \to (2, 4)$$$, $$$(2, 4) \to (4, 4)$$$, $$$(3, 1) \to (3, 3)$$$, $$$(1, 2) \to (1, 1)$$$ 

Sample Inputs:
4
3 1
2 3
3 2
2 1
1 2
5 3
2 3
3 1
1 2
5 4
4 5
5 1
2 2
3 3

Sample Outputs:
1
3
4
2

Solution (Python):
T = int(input())
for iw in range(T):
    (n, m) = map(int, input().split())
    ans = 0
    X = [-1] * (n + 1)
    for i in range(m):
        (x, y) = map(int, input().split())
        if x != y:
            ans += 1
            X[x] = y
    for i in range(1, n + 1):
        if X[i] > 0:
            t = i
            while X[i] > 0:
                x = X[i]
                X[i] = 0
                i = x
                if i == t:
                    ans += 1
                    break
    print(ans)

Solution (Python):
import math
import sys
import bisect
from heapq import heapify, heappop, heappush
from itertools import *
from collections import deque, OrderedDict
from pprint import pprint
sys.setrecursionlimit(10 ** 6)
II = lambda : sys.stdin.readline()
inp = lambda : int(II())
inpm = lambda : map(int, II().split())
inpl = lambda : list(inpm())
DEBUG = False

def debug(**kwargs):
    if DEBUG:
        arr = []
        for (k, w) in kwargs.items():
            arr.append(f'{k}: {w}')
        print(*arr, sep=', ')
(WHITE, GREY, BLACK, RED) = (0, 1, 2, 3)
EPS = 1e-09
INF = int(1e+18)
MOD = int(1000000000.0) + 7
N = 2000009
''

def solve():
    (n, m) = inpm()
    a = [-1] + [-1 for _ in range(n)]
    b = [-1] + [WHITE for _ in range(n)]
    for _ in range(m):
        (x, y) = inpm()
        if x == y:
            b[x] = BLACK
            continue
        a[x] = y
    res = 0
    for i in range(1, n + 1):
        if b[i] == BLACK:
            continue
        cnt = 0
        x = a[i]
        b[i] = BLACK
        debug(i=i)
        while x != -1 and b[x] != BLACK:
            debug(x=x)
            b[x] = BLACK
            cnt += 1
            x = a[x]
        if x != -1:
            cnt += 1
        if x == i:
            cnt += 1
        debug(cnt=cnt, x=x)
        res += cnt
    print(res)
''

def main():
    t = inp()
    for i in range(t):
        solve()
main()

Solution (Python):
for _ in range(int(input())):
    (n, m) = [int(x) for x in input().split()]
    l = [None] * (n + 1)
    for _ in range(m):
        (x, y) = [int(x) for x in input().split()]
        l[x] = y
    ans = 0
    visited = [False] * (n + 1)
    for i in range(n + 1):
        if l[i] == None or l[i] == i or visited[i]:
            continue
        (tmp, cnt) = (l[i], 1)
        visited[i] = True
        while tmp != i and tmp != None and (visited[tmp] == False):
            visited[tmp] = True
            tmp = l[tmp]
            cnt += 1
        cnt += tmp == i
        cnt -= tmp == None
        ans += cnt
    print(ans)