Problem solution · Python

ABC256 E — Takahashi's Anguish

ABC256 E — Takahashi's Anguish: 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
167 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 ABC256 E — Takahashi's Anguish, 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

  • 167 lines of Python from the credited upstream file abc256_e.py.
  • The implementation visibly relies on sequence storage, hash lookup, ordered lookup.
  • 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 codeABC256 E — Takahashi's Anguish · PythonPython
Use this to learn the idea, then write your own version.
# -*- coding: utf-8 -*-  import sysimport typing sys.setrecursionlimit(10 ** 7)  # 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 = int(input())    x = list(map(int, input().split()))    c = list(map(int, input().split()))    scc = SCCGraph(n)        # グラフの問題として捉える    # functional graph    # グラフの形状から、サイクルがある部分で1か所矛盾が生じるため、コストが増加    # サイクルの検出: 強連結成分分解 & 分解した連結成分の長さが2以上    # サイクルに含まれる頂点のうち、最小のコストを答えに加算    for i, xi in enumerate(x):        xi -= 1        scc.add_edge(i, xi)        groups = scc.scc()    ans = 0     for group in groups:        if len(group) == 1:            continue            ans += min([c[g] for g in group])        print(ans)  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 ↗