#ifndef DECREMENTAL_SCC_H_ #define DECREMENTAL_SCC_H_ #include "algorithm/roditty_zwick.h" #include "algorithm/tarjan.h" #include "graph/breadth_first_tree.h" using namespace graph; namespace algo { template class DecrementalSCC : public RodittyZwick { public: DecrementalSCC() = default; DecrementalSCC(Digraph G) : G(G) {} void init(); void findSCC(); // Return true if u and v are in the same SCC bool query(const T& u, const T& v); // Remove edge (u,v) and update A accordingly for fast checking query void remove(const T& u, const T& v); void setGraph(Digraph G); private: Digraph G; // Array used to answer strong connectivity queries in O(1) time std::map A; // Maintain in-out bfs trees std::map> inTree; std::map> outTree; // Connect each representative with its SCC std::map> SCC; }; template void DecrementalSCC::init() { findSCC(); } template void DecrementalSCC::findSCC() { auto SCCs = Tarjan(G.adjMatrix).execute(); for (auto& C : SCCs) { const auto& w = C.id; for (const auto& v : std::views::keys(C.adjMatrix)) A[v] = w; outTree[w] = BreadthFirstTree(C, w); inTree[w] = BreadthFirstTree(C.reverse(), w); SCC[w] = C; } } template bool DecrementalSCC::query(const T& u, const T& v) { return A[u] == A[v]; } template void DecrementalSCC::remove(const T& u, const T& v) { const auto& w = A[u]; SCC[w].remove(u, v); G.remove(u, v); // If u and v are not in the same SCC, do nothing if (A[u] != A[v]) return; // If edge (u,v) is not contained in both inTree and outTree do nothing if (!inTree[w].adjMatrix[u].contains(v) && !outTree[w].adjMatrix[u].contains(v)) return; // Update In(w) and Out(w) outTree[w] = BreadthFirstTree(SCC[w], w); inTree[w] = BreadthFirstTree(SCC[w].reverse(), w); // If a SCC is broken, compute all SCCs again if (!inTree[w].adjMatrix.count(u) || !outTree[w].adjMatrix.count(v)) findSCC(); } template void DecrementalSCC::setGraph(Digraph G) { this->G = G; } }; // namespace algo #endif