Files
reachability-algorithms/include/algorithm/frigioni.h
2024-08-03 13:14:42 +03:00

234 lines
6.0 KiB
C++

#ifndef FRIGIONI_H_
#define FRIGIONI_H_
#include "algorithm/decremental_reachability.h"
#include "algorithm/roditty_zwick.h"
#include <utility>
namespace algo {
template <typename T> class Frigioni : public DecrementalReachability<T> {
public:
Frigioni() = default;
explicit Frigioni(graph::Digraph<T> 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<std::pair<T, T>> &edges) override;
private:
// Transitive closure matrix, used to answer reachability queries in O(1)
std::unordered_map<T, std::unordered_map<T, bool>> TC;
// For each SCC, store a reachability tree created as a BFS tree
std::unordered_map<SCC<T>, BreadthFirstTree<T>, HashSCC<T>> RT;
// For each SCC, store collections of incoming, outgoing and internal edges
struct Edges {
std::set<std::pair<T, T>> in;
std::set<std::pair<T, T>> inc;
std::set<std::pair<T, T>> out;
};
std::unordered_map<SCC<T>, Edges, HashSCC<T>> E;
// Structure that keeps candidate components to use as a hook
std::unordered_map<SCC<T>, std::stack<T>, HashSCC<T>> H;
// Decremental maintenance of strongly connected components
RodittyZwick<T> 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<T, SCC<T>> &L,
std::unordered_map<T, SCC<T>> C, const T &w);
};
template <typename T> void Frigioni<T>::init() {
rodittyZwick = RodittyZwick<T>(this->G);
rodittyZwick.init();
auto C = rodittyZwick.getComponentMap();
for (const auto &w : std::views::keys(C)) {
RT[C[w]] = BreadthFirstTree<T>(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 <typename T> bool Frigioni<T>::query(const T &u, const T &v) {
return TC[u][v];
}
template <typename T>
void Frigioni<T>::remove(const std::vector<std::pair<T, T>> &edges) {
std::vector<std::pair<T, T>> Eint;
std::vector<std::pair<T, T>> 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 <typename T> void Frigioni<T>::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 <typename T> void Frigioni<T>::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 <typename T> void Frigioni<T>::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 <typename T>
void Frigioni<T>::splitEdges(std::unordered_map<T, SCC<T>> &L,
std::unordered_map<T, SCC<T>> 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