234 lines
6.0 KiB
C++
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 |