Problem solution · Python

ABC245 F — Endless Walk

ABC245 F — Endless Walk: a Python solution using depth-first search. Learn the idea, check the complexity, and read the full code, with credit to KATO-Hiro AtCoder Solutions.

Technique
Depth-first search
Source
KATO-Hiro AtCoder Solutions
Length
171 lines
Start with the idea.

Try the problem first. If you get stuck, read the approach below, then write your own solution. The full code is at the bottom.

Approach

Depth-first search

For ABC245 F — Endless Walk, the implementation follows one branch at a time, making it suitable for components, trees, backtracking, or dependency exploration.

  1. Define the state carried into one recursive or stack frame.
  2. Mark or choose the current state before exploring children.
  3. Combine child results or undo the choice when the branch finishes.

Code notes

  • 171 lines of Python from the credited upstream file abc245_f.py.
  • The implementation visibly relies on sequence storage, hash lookup, ordered lookup, cached states.
  • No explicit loop blocks detected, together with recursive traversal.

Complexity

Count unique states for graph traversal; for backtracking, count the branching factor and maximum depth.

Check the problem constraints before deciding whether this complexity will pass.

Source

Code and credit

This code comes from KATO-Hiro AtCoder Solutions by KATO-Hiro and is used under the CC0-1.0 licence.

Full codeABC245 F — Endless Walk · PythonPython
Use this to learn the idea, then write your own version.
# -*- coding: utf-8 -*-  import sysimport typing  # See:# https://github.com/not522/ac-library-python/tree/master/atcoderclass SCCGraph:    def __init__(self, n: int = 0) -> None:        self._internal = _SCCGraph(n)     def add_edge(self, from_vertex: int, to_vertex: int) -> None:        n = self._internal.num_vertices()         assert 0 <= from_vertex < n        assert 0 <= to_vertex < n         self._internal.add_edge(from_vertex, to_vertex)     def scc(self) -> typing.List[typing.List[int]]:        return self._internal.scc()  class CSR:    def __init__(            self, n: int, edges: typing.List[typing.Tuple[int, int]]) -> None:        self.start = [0] * (n + 1)        self.elist = [0] * len(edges)         for e in edges:            self.start[e[0] + 1] += 1         for i in range(1, n + 1):            self.start[i] += self.start[i - 1]         counter = self.start.copy()         for e in edges:            self.elist[counter[e[0]]] = e[1]            counter[e[0]] += 1  class _SCCGraph:    '''    Reference:    R. Tarjan,    Depth-First Search and Linear Graph Algorithms    '''     def __init__(self, n: int) -> None:        self._n = n        self._edges: typing.List[typing.Tuple[int, int]] = []     def num_vertices(self) -> int:        return self._n     def add_edge(self, from_vertex: int, to_vertex: int) -> None:        self._edges.append((from_vertex, to_vertex))     def scc_ids(self) -> typing.Tuple[int, typing.List[int]]:        g = CSR(self._n, self._edges)        now_ord = 0        group_num = 0        visited = []        low = [0] * self._n        order = [-1] * self._n        ids = [0] * self._n         sys.setrecursionlimit(max(self._n + 1000, sys.getrecursionlimit()))         def dfs(v: int) -> None:            nonlocal now_ord            nonlocal group_num            nonlocal visited            nonlocal low            nonlocal order            nonlocal ids             low[v] = now_ord            order[v] = now_ord            now_ord += 1            visited.append(v)             for i in range(g.start[v], g.start[v + 1]):                to = g.elist[i]                 if order[to] == -1:                    dfs(to)                    low[v] = min(low[v], low[to])                else:                    low[v] = min(low[v], order[to])             if low[v] == order[v]:                while True:                    u = visited[-1]                    visited.pop()                    order[u] = self._n                    ids[u] = group_num                     if u == v:                        break                 group_num += 1         for i in range(self._n):            if order[i] == -1:                dfs(i)         for i in range(self._n):            ids[i] = group_num - 1 - ids[i]         return group_num, ids     def scc(self) -> typing.List[typing.List[int]]:        ids = self.scc_ids()        group_num = ids[0]        counts = [0] * group_num         for x in ids[1]:            counts[x] += 1         groups: typing.List[typing.List[int]] = [[] for _ in range(group_num)]         for i in range(self._n):            groups[ids[1][i]].append(i)         return groups      def main():    import sys     input = sys.stdin.readline     n, m = map(int, input().split())    to = [[] for _ in range(n)]    scc = SCCGraph(n)        for _ in range(m):        ai, bi = map(int, input().split())        ai -= 1        bi -= 1            scc.add_edge(ai, bi)        to[bi].append(ai)     groups = scc.scc()    dp = [0] * (n + 1)     # 頂点が2つ以上ある = サイクルがある    for group in groups:        if len(group) <= 1:            continue         for v in group:            dp[v] = 1     # 強連結成分分解した結果がトポロジカル順になっており、後ろから見る    for group in groups[::-1]:        for x in group:            for y in to[x]:                dp[y] |= dp[x]     print(sum(dp))  if __name__ == "__main__":    main() 

Did this explanation save you time? I'm a Grade 11 student building this free library to make difficult algorithms easier to understand.

Buy me a coffee ↗