Desk Disorder (Codeforces 859E)
Tags: Others, dsu

A new set of desks just arrived, and it's about time! Things were getting quite cramped in the office. You've been put in charge of creating a new seating chart for the engineers. The desks are numbered, and you sent out a survey to the engineering team asking each engineer the number of the desk they currently sit at, and the number of the desk they would like to sit at (which may be the same as their current desk). Each engineer must either remain where they sit, or move to the desired seat they indicated in the survey. No two engineers currently sit at the same desk, nor may any two engineers sit at the same desk in the new seating arrangement.How many seating arrangements can you create that meet the specified requirements? The answer may be very large, so compute it modulo 1000000007 = 109 + 7.

Input: Input will begin with a line containing N (1 ≤ N ≤ 100000), the number of engineers. N lines follow, each containing exactly two integers. The i-th line contains the number of the current desk of the i-th engineer and the number of the desk the i-th engineer wants to move to. Desks are numbered from 1 to 2·N. It is guaranteed that no two engineers sit at the same desk.

Output: Print the number of possible assignments, modulo 1000000007 = 109 + 7.

Note: These are the possible assignments for the first example:   1 5 3 7  1 2 3 7  5 2 3 7  1 5 7 3  1 2 7 3  5 2 7 3 

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

5
1 10
2 10
3 10
4 10
5 5

Sample Outputs:
6

5

Solution (Python):
n = int(input())
m = 2 * n + 1
u = [[] for i in range(m)]
v = [0] * m
s = [0] * m
d = 10 ** 9 + 7
y = 1
for j in range(n):
    (a, b) = map(int, input().split())
    v[a] = b
    if a != b:
        s[b] += 1
        u[b].append(a)
for b in range(m):
    if not v[b]:
        x = 0
        p = [b]
        while p:
            x += 1
            a = p.pop()
            s[a] = -1
            p += u[a]
        y = x * y % d
for a in range(m):
    if s[a] == 0:
        b = v[a]
        while s[b] == 1:
            s[b] = -1
            b = v[b]
        s[b] -= 1
for a in range(m):
    if s[a] == 1:
        y = 2 * y % d
        while s[a]:
            s[a] = 0
            a = v[a]
print(y)

Solution (Python):
import sys
input = sys.stdin.readline

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
cyc = []
n = int(input())
f = [-1] * (2 * n)
zz = [0] * (2 * n)
UF = DisjointSetUnion(2 * n)
bad = []
for _ in range(n):
    (u, v) = map(int, input().split())
    u -= 1
    v -= 1
    f[u] = v
    if u == v:
        bad.append(u)
    if UF.find(u) != UF.find(v):
        UF.union(u, v)
    else:
        cyc.append(u)
for v in cyc:
    zz[UF.find(v)] = 1
for v in bad:
    zz[UF.find(v)] = 2
out = 1
MOD = 10 ** 9 + 7
for i in range(2 * n):
    if UF.find(i) == i:
        if zz[i] == 0:
            out *= UF.size[i]
        elif zz[i] == 1:
            out *= 2
        out %= MOD
print(out)

Solution (Python):
import sys
import math
from math import ceil
from functools import lru_cache
input = sys.stdin.readline

def womais(i, j):
    if i == 0:
        return j
    elif j == 0:
        return i
    else:
        return min(i, j)

class DisjointSetUnion:

    def __init__(self, n):
        self.parent = list(range(n))
        self.size = [1] * n
        self.cyc = [0] * 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]
            self.cyc[a] = womais(self.cyc[a], self.cyc[b])
        else:
            self.cyc[a] = womais(self.cyc[a], 2)

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

    def __len__(self):
        return self.num_sets

def ii():
    return int(input())

def li():
    return list(map(lambda x: int(x) - 1, input().split()))
n = ii()
shit = DisjointSetUnion(2 * n)
for _ in range(n):
    (u, v) = li()
    if u == v:
        shit.cyc[shit.find(u)] = 1
    else:
        shit.union(u, v)
ans = 1
seen = set()
for i in range(2 * n):
    a = shit.find(i)
    if a not in seen:
        seen.add(a)
        if shit.cyc[a]:
            ans *= shit.cyc[a]
        else:
            ans *= shit.size[a]
        ans %= int(1000000000.0 + 7)
print(ans)