AtCoder problem 021

C++ solution:
#include <iostream>

#include <vector>

#include <algorithm>

using namespace std;



// Input

int N, M;

int A[1 << 18], B[1 << 18];



// Graph

bool used[1 << 18];

vector<int> G[1 << 18];

vector<int> H[1 << 18];

vector<int> I;

long long cnts = 0;



void dfs(int pos) {

	used[pos] = true;

	for (int i : G[pos]) {

		if (used[i] == false) dfs(i);

	}

	I.push_back(pos);

}



void dfs2(int pos) {

	used[pos] = true;

	cnts++;

	for (int i : H[pos]) {

		if (used[i] == false) dfs2(i);

	}

}



int main() {

	// Step #1. Input

	cin >> N >> M;

	for (int i = 1; i <= M; i++) {

		cin >> A[i] >> B[i];

		G[A[i]].push_back(B[i]);

		H[B[i]].push_back(A[i]);

	}



	// Step #2. First DFS

	for (int i = 1; i <= N; i++) {

		if (used[i] == false) dfs(i);

	}



	// Step #3. Second DFS

	long long Answer = 0;

	reverse(I.begin(), I.end());

	for (int i = 1; i <= N; i++) used[i] = false;

	for (int i : I) {

		if (used[i] == true) continue;

		cnts = 0;

		dfs2(i);

		Answer += cnts * (cnts - 1LL) / 2LL;

	}



	// Step #4. Output The Answer!

	cout << Answer << endl;

	return 0;

}


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)