#ifndef FRIGIONI_H_ #define FRIGIONI_H_ #include "algorithm/decremental_reachability.h" #include "algorithm/roditty_zwick.h" #include namespace algo { template class Frigioni : public DecrementalReachability { public: Frigioni() = default; explicit Frigioni(graph::Digraph G) { this->G = G; } // Initialize the decremental maintenance data structure for general graphs void init() override; // Execute reachability query q(u, v) from vertex u to vertex v // in O(1) using the transitive closure matrix bool query(const T& u, const T& v) override; // Delete set of edges and explicitely maintain the transitive closure void remove(const std::vector>& edges) override; private: // Transitive closure matrix, used to answer reachability queries in O(1) std::unordered_map> TC; // For each SCC, store a reachability tree created as a BFS tree std::unordered_map, BreadthFirstTree, HashSCC> RT; // For each SCC, store collections of incoming, outgoing and internal edges struct Edges { std::set> in; std::set> inc; std::set> out; }; std::unordered_map, Edges, HashSCC> E; // Structure that keeps candidate components to use as a hook std::unordered_map, std::stack, HashSCC> H; // Decremental maintenance of strongly connected components RodittyZwick rodittyZwick; // Delete internal edge e(u, v) void removeInternal(const T& u, const T& v); // Delete external edge e(u, v) void removeExternal(const T& u, const T& v); // Repair reachability trees void repairTrees(); // void splitEdges(std::unordered_map>& L, std::unordered_map> C, const T& w); }; template void Frigioni::init() { rodittyZwick = RodittyZwick(this->G); rodittyZwick.init(); auto C = rodittyZwick.getComponentMap(); for (const auto& w : std::views::keys(C)) { RT[C[w]] = BreadthFirstTree(this->G, w); for (const auto& u : this->G.vertices()) { for (const auto& v : this->G.adjList[u]) { if (!C[w].contains(u) && C[w].contains(v)) E[C[w]].inc.insert({ u, v }); else if (C[w].contains(u) && !C[w].contains(v)) E[C[w]].out.insert({ u, v }); else E[C[w]].in.insert({ u, v }); TC[u][v] = false; } } } for (const auto& w : std::views::keys(C)) { for (const auto& u : C[w].vertices()) { for (const auto& v : RT[C[w]].vertices()) { TC[u][v] = true; } } } } template bool Frigioni::query(const T& u, const T& v) { return TC[u][v]; } template void Frigioni::remove(const std::vector>& edges) { std::vector> Eint; std::vector> Eext; for (const auto& [u, v] : edges) { if (!this->G.adjList[u].contains(v)) continue; if (rodittyZwick.query(u, v)) Eint.push_back({ u, v }); else Eext.push_back({ u, v }); } for (const auto& [u, v] : Eint) removeInternal(u, v); for (const auto& [u, v] : Eext) removeExternal(u, v); repairTrees(); } template void Frigioni::removeInternal(const T& u, const T& v) { this->G.remove(u, v); auto L = rodittyZwick.getComponentMap(); rodittyZwick.remove(u, v); auto C = rodittyZwick.getComponentMap(); if (rodittyZwick.query(u, v)) { E[C[u]].in.erase({ u, v }); return; } if (L[u] == C[u]) { E[L[u]].out.erase({ u, v }); E.erase(L[v]); splitEdges(L, C, v); } else { E[L[v]].inc.erase({ u, v }); E.erase(L[u]); splitEdges(L, C, u); } for (const auto& w : std::views::keys(C)) { if (!RT[C[w]].contains(v)) continue; RT[C[w]].removeEdgeTo(v); if (E[C[v]].inc.size() > 0) { H[C[w]].push(v); /*h mipos C[v].id?*/ continue; } for (const auto& x : C[w].vertices()) { for (const auto& y : C[v].vertices()) { TC[x][y] = false; } } for (const auto& [a, b] : E[C[v]].out) H[C[w]].push(b); } } template void Frigioni::removeExternal(const T& u, const T& v) { this->G.remove(u, v); auto C = rodittyZwick.getComponentMap(); E[C[v]].inc.erase({ u, v }); E[C[u]].out.erase({ u, v }); for (const auto& w : std::views::keys(C)) { if (!RT[C[w]].contains(v)) continue; RT[C[w]].removeEdgeTo(v); if (E[C[v]].inc.size() > 0) { H[C[w]].push(v); continue; } for (const auto& x : C[w].vertices()) { for (const auto& y : C[v].vertices()) { TC[x][y] = false; } } for (const auto& [a, b] : E[C[v]].out) H[C[w]].push(b); } } template void Frigioni::repairTrees() { auto C = rodittyZwick.getComponentMap(); for (const auto& w : std::views::keys(C)) { while (H[C[w]].size() > 0) { const auto& h = H[C[w]].top(); H[C[w]].pop(); bool foundHook = false; for (const auto& [a, b] : E[C[h]].inc) { if (RT[C[w]].adjList[w].contains(a)) { RT[C[w]].insert(a, h); foundHook = true; break; } } if (foundHook) continue; for (const auto& x : C[w].vertices()) { for (const auto& y : C[h].vertices()) { TC[x][y] = false; } } for (const auto& [a, b] : E[C[h]].out) { if (RT[C[w]].adjList[h].contains(a)) { H[C[w]].push(b); } } } } } template void Frigioni::splitEdges(std::unordered_map>& L, std::unordered_map> C, const T& w) { for (const auto& [a, b] : E[L[w]].inc) E[C[b]].inc.insert({ a, b }); for (const auto& [a, b] : E[L[w]].out) E[C[a]].out.insert({ a, b }); for (const auto& [a, b] : E[L[w]].in) { if (C[a] == C[b]) { E[C[a]].in.insert({ a, b }); } else { E[C[a]].out.insert({ a, b }); E[C[b]].in.insert({ a, b }); } } } } // namespace algo #endif