#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