@@ -37,31 +37,73 @@ namespace cp_algo::graph {
3737 template <template <typename > class Context , graph_type graph>
3838 auto tarjan (graph const & g) {
3939 Context<graph> context (g);
40- auto dfs = [&](this auto &&dfs, node_index v, edge_index ep = -1 ) -> void {
41- context.state [v] = visiting;
42- context.tin [v] = context.low [v] = context.timer ++;
43- context.stack .push (v);
44- for (auto e: g.outgoing (v)) {
45- if constexpr (undirected_graph_type<graph>) {
46- if (ep == graph::opposite_idx (e)) {
47- continue ;
48- }
40+
41+ auto const & adj = g.incidence_lists ();
42+
43+ struct frame {
44+ node_index v;
45+ edge_index ep;
46+ int sv; // edge index in stack_union
47+ enum { INIT, PROCESS_EDGES, HANDLE_CHILD } state;
48+ };
49+
50+ std::stack<frame> dfs_stack;
51+
52+ for (auto root: g.nodes ()) {
53+ if (context.state [root] != unvisited) continue ;
54+
55+ dfs_stack.push ({root, -1 , 0 , frame::INIT});
56+
57+ while (!dfs_stack.empty ()) {
58+ auto & f = dfs_stack.top ();
59+
60+ if (f.state == frame::INIT) {
61+ context.state [f.v ] = visiting;
62+ context.tin [f.v ] = context.low [f.v ] = context.timer ++;
63+ context.stack .push (f.v );
64+ f.sv = adj.head [f.v ];
65+ f.state = frame::PROCESS_EDGES;
4966 }
50- node_index u = g.edge (e).to ;
51- if (context.state [u] == unvisited) {
52- dfs (u, e);
53- context.low [v] = std::min (context.low [v], context.low [u]);
54- context.on_tree_edge (v, u);
55- } else if (context.state [u] != blocked) {
56- context.low [v] = std::min (context.low [v], context.tin [u]);
67+
68+ if (f.state == frame::HANDLE_CHILD) {
69+ auto e = adj.data [f.sv ];
70+ f.sv = adj.next [f.sv ];
71+ node_index u = g.edge (e).to ;
72+ context.low [f.v ] = std::min (context.low [f.v ], context.low [u]);
73+ context.on_tree_edge (f.v , u);
74+ f.state = frame::PROCESS_EDGES;
5775 }
58- }
59- context.state [v] = visited;
60- context.on_exit (v);
61- };
62- for (auto v: g.nodes ()) {
63- if (context.state [v] == unvisited) {
64- dfs (v);
76+
77+ // PROCESS_EDGES
78+ bool found_child = false ;
79+ while (f.sv != 0 ) {
80+ auto e = adj.data [f.sv ];
81+
82+ if constexpr (undirected_graph_type<graph>) {
83+ if (f.ep == graph::opposite_idx (e)) {
84+ f.sv = adj.next [f.sv ];
85+ continue ;
86+ }
87+ }
88+
89+ node_index u = g.edge (e).to ;
90+ if (context.state [u] == unvisited) {
91+ f.state = frame::HANDLE_CHILD;
92+ dfs_stack.push ({u, e, 0 , frame::INIT});
93+ found_child = true ;
94+ break ;
95+ } else if (context.state [u] != blocked) {
96+ context.low [f.v ] = std::min (context.low [f.v ], context.tin [u]);
97+ }
98+ f.sv = adj.next [f.sv ];
99+ }
100+
101+ if (found_child) continue ;
102+
103+ // All edges processed
104+ context.state [f.v ] = visited;
105+ context.on_exit (f.v );
106+ dfs_stack.pop ();
65107 }
66108 }
67109 return context.components ;
0 commit comments