AtCoder problem 021

C++ solution:


Python solution: import sys
input = sys.stdin.readline

class SCC:
    def __init__(self, n):
        self.n = n
        self.graph = [[] for _ in range(n)]
        self.rev_graph = [[] for _ in range(n)]
        self.labels = [-1] * n
        self.lb_cnt = 0

    def add_edge(self, v, nxt_v):
        self.graph[v].append(nxt_v)
        self.rev_graph[nxt_v].append(v)

    def build(self):
        self.post_order = []
        self.used = [False] * self.n
        for v in range(self.n):
            if not self.used[v]:
                self._dfs(v)
        for v in reversed(self.post_order):
            if self.labels[v] == -1:
                self._rev_dfs(v)
                self.lb_cnt += 1

    def _dfs(self, v):
        stack = [v, 0]
        while stack:
            v, idx = stack[-2:]
            if not idx and self.used[v]:
                stack.pop()
                stack.pop()
            else:
                self.used[v] = True
                if idx < len(self.graph[v]):
                    stack[-1] += 1
                    stack.append(self.graph[v][idx])
                    stack.append(0)
                else:
                    stack.pop()
                    self.post_order.append(stack.pop())

    def _rev_dfs(self, v):
        stack = [v]
        self.labels[v] = self.lb_cnt
        while stack:
            v = stack.pop()
            for nxt_v in self.rev_graph[v]:
                if self.labels[nxt_v] != -1:
                    continue
                stack.append(nxt_v)
                self.labels[nxt_v] = self.lb_cnt

    def construct(self):
        self.dag = [[] for i in range(self.lb_cnt)]
        self.groups = [[] for i in range(self.lb_cnt)]
        for v, lb in enumerate(self.labels):
            for nxt_v in self.graph[v]:
                nxt_lb = self.labels[nxt_v]
                if lb == nxt_lb:
                    continue
                self.dag[lb].append(nxt_lb)
            self.groups[lb].append(v)
        return self.dag, self.groups

n, m = map(int, input().split())
graph = [list(map(int,input().split())) for _ in range(m)]
scc = SCC(n)

for u, v in graph:
    scc.add_edge(u - 1, v - 1)
scc.build()
_,elems = scc.construct()

ans = 0
for i in range(len(elems)):
    ans += len(elems[i]) * (len(elems[i]) - 1) // 2

print(ans)



Kuin solution: func main()
	var n: int :: cui@inputInt()
	var m: int :: cui@inputInt()
	var ab: []list<int> :: #[n]list<int>
	var ba: []list<int> :: #[n]list<int>
	for i(0, n - 1)
		do ab[i] :: #list<int>
		do ba[i] :: #list<int>
	end for
	for(1, m)
		var a: int :: cui@inputInt() - 1
		var b: int :: cui@inputInt() - 1
		do ab[a].add(b)
		do ba[b].add(a)
	end for
	
	var used1: []bool :: #[n]bool
	var ordersList: list<int> :: #list<int>
	for i(0, n - 1)
		if(!used1[i])
			do dfs1(ab, used1, i, ordersList)
		end if
	end for
	var orders: []int :: ordersList.toArray()
	do orders.reverse()
	
	var used2: []bool :: #[n]bool
	var ans: int :: 0
	for i(0, n - 1)
		if(!used2[orders[i]])
			var res: int :: dfs2(ba, used2, orders[i])
			do ans :+ res * (res - 1) / 2
		end if
	end for
	do cui@print("\{ans}\n")
	
	func dfs1(ab: []list<int>, used: []bool, u: int, ordersList: list<int>)
		do used[u] :: true
		do ab[u].head()
		for i(0, ^ab[u] - 1)
			var v: int :: ab[u].get()
			if(!used[v])
				do dfs1(ab, used, v, ordersList)
			end if
			do ab[u].next()
		end for
		do ordersList.add(u)
	end func
	
	func dfs2(ba: []list<int>, used: []bool, u: int): int
		var res: int :: 1
		do used[u] :: true
		do ba[u].head()
		for i(0, ^ba[u] - 1)
			var v: int :: ba[u].get()
			if(!used[v])
				do res :+ dfs2(ba, used, v)
			end if
			do ba[u].next()
		end for
		ret res
	end func
end func