|
| 1 | +#pragma once |
| 2 | + |
| 3 | +#include <algorithm> |
| 4 | +#include <tuple> |
| 5 | +#include <utility> |
| 6 | +#include <vector> |
| 7 | + |
| 8 | +#include "graph/strongly_connected_components.hpp" |
| 9 | + |
| 10 | +// edges[i] = (s, t) means that the edge (s, t) is added at i-th tick. |
| 11 | +// Return the earliest tick when the edge (s, t) is included in a cycle. |
| 12 | +// If the edge (s, t) is never included in a cycle or s == t, return M. |
| 13 | +// Complexity: O(M log M), where M = edges.size() |
| 14 | +// Verified: https://codeforces.com/contest/1989/submission/268026664 |
| 15 | +std::vector<int> incremental_scc(const std::vector<std::pair<int, int>> &edges) { |
| 16 | + int N = 1; |
| 17 | + for (auto [s, t] : edges) N = std::max({N, s + 1, t + 1}); |
| 18 | + |
| 19 | + const int M = edges.size(); |
| 20 | + |
| 21 | + std::vector<int> ret(M, M); |
| 22 | + |
| 23 | + std::vector<int> compressed_idx(N, -1); |
| 24 | + |
| 25 | + using Edges = std::vector<std::tuple<int, int, int>>; |
| 26 | + |
| 27 | + auto rec = [&](auto &&self, const Edges &e, int tl, int tr) -> void { |
| 28 | + if (e.empty() or tl + 1 == tr) return; |
| 29 | + |
| 30 | + int n = 0; |
| 31 | + for (const auto &[tick, s, t] : e) { |
| 32 | + if (compressed_idx.at(s) == -1) compressed_idx.at(s) = n++; |
| 33 | + if (compressed_idx.at(t) == -1) compressed_idx.at(t) = n++; |
| 34 | + } |
| 35 | + |
| 36 | + const int tmid = (tl + tr) / 2; |
| 37 | + |
| 38 | + DirectedGraphSCC scc(n); |
| 39 | + for (const auto &[tick, s, t] : e) { |
| 40 | + if (tick < tmid) scc.add_edge(compressed_idx.at(s), compressed_idx.at(t)); |
| 41 | + } |
| 42 | + scc.FindStronglyConnectedComponents(); |
| 43 | + |
| 44 | + Edges left, right; |
| 45 | + |
| 46 | + for (const auto &[tick, s, t] : e) { |
| 47 | + const int sc = compressed_idx.at(s), tc = compressed_idx.at(t); |
| 48 | + if (tick < tmid and scc.cmp.at(sc) == scc.cmp.at(tc)) { |
| 49 | + ret.at(tick) = tmid - 1; |
| 50 | + left.emplace_back(tick, sc, tc); |
| 51 | + } else { |
| 52 | + right.emplace_back(tick, scc.cmp.at(sc), scc.cmp.at(tc)); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + for (auto [_, s, t] : e) compressed_idx.at(s) = compressed_idx.at(t) = -1; |
| 57 | + |
| 58 | + self(self, left, tl, tmid); |
| 59 | + self(self, right, tmid, tr); |
| 60 | + }; |
| 61 | + |
| 62 | + Edges init; |
| 63 | + init.reserve(M); |
| 64 | + for (int tick = 0; tick < M; ++tick) { |
| 65 | + if (auto [s, t] = edges.at(tick); s != t) init.emplace_back(tick, s, t); |
| 66 | + } |
| 67 | + |
| 68 | + rec(rec, init, 0, M + 1); |
| 69 | + |
| 70 | + return ret; |
| 71 | +} |
0 commit comments