Skip to content

Commit 2fe6fe4

Browse files
committed
Store endpoint xor in edges
1 parent 724b253 commit 2fe6fe4

14 files changed

+121
-126
lines changed

cp-algo/graph/base.hpp

Lines changed: 13 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,21 @@ namespace cp_algo::graph {
1818
graph<edge_t, mode> gt(n(), v0);
1919
for(auto v: nodes()) {
2020
for(auto e: outgoing(v)) {
21-
node_index u = edge(e).to;
22-
gt.add_edge(u, edge(e).backedge(v));
21+
gt.add_edge(edge(e).traverse(v), edge(e).backedge());
2322
}
2423
}
2524
return gt;
2625
}
2726
void add_edge(node_index u, edge_t e) {
28-
adj.push(u, (edge_index)size(E));
27+
edge_index idx = (edge_index)size(E);
2928
E.push_back(e);
29+
adj.push(u, idx);
3030
if constexpr (mode == undirected) {
31-
adj.push(e.to, (edge_index)size(E));
32-
E.push_back(e.backedge(u));
31+
adj.push(e.traverse(u), idx);
3332
}
3433
}
3534
void add_edge(node_index u, auto... Args) {
36-
add_edge(u, edge_t(Args...));
35+
add_edge(u, edge_t(u, Args...));
3736
}
3837
void read_edges(node_index m) {
3938
adj.reserve(mode == undirected ? 2 * m : m);
@@ -42,45 +41,14 @@ namespace cp_algo::graph {
4241
add_edge(u, e);
4342
}
4443
}
45-
auto outgoing(node_index v) const {
46-
return adj[v];
47-
}
48-
auto nodes() const {
49-
return std::views::iota(node_index(0), n());
50-
}
51-
52-
auto edges() const {
53-
if constexpr (mode == undirected) {
54-
return E | std::views::stride(2);
55-
} else {
56-
return E | std::views::all;
57-
}
58-
}
59-
auto edge_indices() const {
60-
auto indices = std::views::iota(edge_index(0), edge_index(size(E)));
61-
if constexpr (mode == undirected) {
62-
return indices | std::views::stride(2);
63-
} else {
64-
return indices;
65-
}
66-
}
67-
incidence_list const& incidence_lists() const {return adj;}
68-
edge_t const& edge(edge_index e) const {return E[e];}
69-
node_index n() const {return (node_index)adj.size();}
70-
edge_index m() const {
71-
return (edge_index)size(edges());
72-
}
73-
static edge_index canonical_idx(edge_index e) {
74-
if constexpr (mode == undirected) {
75-
return e / 2;
76-
} else {
77-
return e;
78-
}
79-
}
80-
static edge_index opposite_idx(edge_index e) {
81-
static_assert(mode == undirected, "opposite_idx is only defined for undirected graphs");
82-
return e ^ 1;
83-
}
44+
auto outgoing(node_index v) const {return adj[v];}
45+
auto edges() const {return E | std::views::all;}
46+
auto nodes() const {return std::views::iota(node_index(0), n());}
47+
auto edge_indices() const {return std::views::iota(edge_index(0), m());}
48+
auto&& incidence_lists(this auto&& self) {return self.adj;}
49+
auto&& edge(this auto&& self, edge_index e) {return self.E[e];}
50+
node_index n() const {return (node_index)incidence_lists().size();}
51+
edge_index m() const {return (edge_index)edges().size();}
8452
private:
8553
node_index v0;
8654
big_vector<edge_t> E;

cp-algo/graph/cycle.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ namespace cp_algo::graph {
2121

2222
void on_back_edge(node_index v, edge_index e) {
2323
if (empty(cycle)) {
24-
v0 = base::g->edge(e).to;
24+
v0 = base::g->edge(e).traverse(v);
2525
base::done = true;
2626
closed = v == v0;
2727
cycle.push_front(e);
@@ -30,9 +30,9 @@ namespace cp_algo::graph {
3030
};
3131

3232
template<graph_type graph>
33-
std::deque<edge_index> find_cycle(graph const& g) {
33+
std::pair<node_index, std::deque<edge_index>> find_cycle(graph const& g) {
3434
auto context = dfs<cycle_context>(g);
35-
return context.cycle;
35+
return {context.v0, context.cycle};
3636
}
3737
}
3838
#endif // CP_ALGO_GRAPH_CYCLE_HPP

cp-algo/graph/dfs.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,13 @@ namespace cp_algo::graph {
8383
auto e = adj.data[f.sv];
8484

8585
if constexpr (undirected_graph_type<graph>) {
86-
if (f.ep == graph::opposite_idx(e)) {
86+
if (f.ep == e) {
8787
f.sv = adj.next[f.sv];
8888
continue;
8989
}
9090
}
9191

92-
node_index u = g.edge(e).to;
92+
node_index u = g.edge(e).traverse(f.v);
9393
if (context.state[u] == unvisited) {
9494
context.on_tree_edge(f.v, e);
9595
f.state = frame::HANDLE_CHILD;

cp-algo/graph/edge_types.hpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,37 +5,44 @@
55
namespace cp_algo::graph {
66
using node_index = int;
77
struct edge_base {
8-
node_index to;
8+
node_index xor_endpoints;
99

1010
edge_base() {}
11-
edge_base(node_index v): to(v) {}
11+
edge_base(node_index from, node_index to): xor_endpoints(from ^ to) {}
12+
13+
// Given one endpoint, return the other
14+
node_index traverse(node_index from) const {
15+
return xor_endpoints ^ from;
16+
}
1217

1318
static auto read(node_index v0 = 0) {
1419
node_index u, v;
1520
std::cin >> u >> v;
16-
return std::pair{u - v0, edge_base(v - v0)};
21+
u -= v0;
22+
v -= v0;
23+
return std::pair{u, edge_base(u, v)};
1724
}
1825

19-
edge_base backedge(int from) const {
20-
return {from};
26+
edge_base backedge() const {
27+
return *this;
2128
}
2229
};
2330

2431
struct weighted_edge: edge_base {
2532
int64_t w;
2633

2734
weighted_edge() {}
28-
weighted_edge(node_index v, int64_t w): edge_base(v), w(w) {}
35+
weighted_edge(node_index from, node_index to, int64_t w): edge_base(from, to), w(w) {}
2936

3037
static auto read(node_index v0 = 0) {
31-
node_index u, v;
38+
auto [u, e] = edge_base::read(v0);
3239
int64_t w;
33-
std::cin >> u >> v >> w;
34-
return std::pair{u - v0, weighted_edge{v - v0, w}};
40+
std::cin >> w;
41+
return std::pair{u, weighted_edge(u, e.traverse(u), w)};
3542
}
3643

37-
weighted_edge backedge(node_index from) const {
38-
return {from, w};
44+
weighted_edge backedge() const {
45+
return *this;
3946
}
4047
};
4148

cp-algo/graph/euler.hpp

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace cp_algo::graph {
1717
deg[v]++;
1818
default_start = v;
1919
if constexpr (digraph_type<graph>) {
20-
deg[g.edge(e).to]--;
20+
deg[g.edge(e).traverse(v)]--;
2121
}
2222
}
2323
}
@@ -51,19 +51,22 @@ namespace cp_algo::graph {
5151
auto const& adj = g.incidence_lists();
5252
auto head = adj.head;
5353

54-
std::stack<edge_index> stack;
55-
stack.push(-1);
54+
struct stack_frame {
55+
edge_index ep;
56+
node_index v;
57+
};
58+
std::stack<stack_frame> stack;
59+
stack.push({-1, v0});
5660

5761
while (!empty(stack)) {
58-
auto ep = stack.top();
59-
node_index w = ~ep ? g.edge(ep).to : v0;
62+
auto [ep, v] = stack.top();
6063
bool found_edge = false;
6164

62-
while (head[w] != 0) {
63-
auto e = adj.data[std::exchange(head[w], adj.next[head[w]])];
64-
if(state[graph::canonical_idx(e)] == unvisited) {
65-
state[graph::canonical_idx(e)] = visited;
66-
stack.push(e);
65+
while (head[v] != 0) {
66+
auto e = adj.data[std::exchange(head[v], adj.next[head[v]])];
67+
if(state[e] == unvisited) {
68+
state[e] = visited;
69+
stack.push({e, g.edge(e).traverse(v)});
6770
found_edge = true;
6871
break;
6972
}

cp-algo/graph/mst.hpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,27 @@ namespace cp_algo::graph {
88
template<weighted_undirected_graph_type graph>
99
std::pair<int64_t, std::vector<edge_index>> mst(graph const& g) {
1010
struct edge {
11-
int64_t w;
12-
edge_index i;
11+
edge_index idx;
12+
node_index v;
1313
};
14-
auto edges = g.edge_indices() | std::ranges::to<std::vector>();
14+
std::vector<edge> edges;
15+
for(auto v: g.nodes()) {
16+
for(auto e: g.outgoing(v)) {
17+
if (v < g.edge(e).traverse(v)) {
18+
edges.push_back({e, v});
19+
}
20+
}
21+
}
1522
radix_sort(edges, [&](auto e) {
16-
return g.edge(e).w;
23+
return g.edge(e.idx).w;
1724
});
1825
structures::dsu me(g.n());
1926
int64_t total = 0;
2027
std::vector<edge_index> mst;
21-
for(auto e: edges) {
22-
if(me.uni(g.edge(e).to, g.edge(graph::opposite_idx(e)).to)) {
23-
total += g.edge(e).w;
24-
mst.push_back(graph::canonical_idx(e));
28+
for(auto [idx, v]: edges) {
29+
if(me.uni(v, g.edge(idx).traverse(v))) {
30+
total += g.edge(idx).w;
31+
mst.push_back(idx);
2532
}
2633
}
2734
return {total, mst};

cp-algo/graph/shortest_path.hpp

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,8 @@
55
#include <queue>
66
namespace cp_algo::graph {
77
struct shortest_path_context {
8-
struct reverse {
9-
node_index u;
10-
edge_index e;
11-
};
128
std::vector<int64_t> dist;
13-
std::vector<reverse> pre;
9+
std::vector<edge_index> pre;
1410
static constexpr int64_t inf = 1e18;
1511
shortest_path_context(int n)
1612
: dist(n, inf), pre(n) {}
@@ -28,7 +24,7 @@ namespace cp_algo::graph {
2824

2925
dijkstra_context(int n) : shortest_path_context(n) {}
3026

31-
void push(node_index, node_index v) {
27+
void push(node_index, edge_index, node_index v) {
3228
pq.push({dist[v], v});
3329
}
3430

@@ -52,7 +48,7 @@ namespace cp_algo::graph {
5248

5349
spfa_context(int n) : shortest_path_context(n), flags(n) {}
5450

55-
void push(node_index, node_index v) {
51+
void push(node_index, edge_index, node_index v) {
5652
if (!(flags[v] & in_queue)) {
5753
que.push(v);
5854
flags[v] |= in_queue;
@@ -73,15 +69,19 @@ namespace cp_algo::graph {
7369
};
7470

7571
struct deep_spfa_context: spfa_context {
76-
std::vector<std::basic_string<node_index>> dependents;
72+
struct traverse_edge {
73+
edge_index e;
74+
node_index v;
75+
};
76+
std::vector<std::basic_string<traverse_edge>> dependents;
7777

7878
deep_spfa_context(int n) : spfa_context(n), dependents(n) {}
7979

80-
void push(node_index u, node_index v) {
80+
void push(node_index u, edge_index e, node_index v) {
8181
invalidate_subtree(v);
82-
dependents[u].push_back(v);
82+
dependents[u].push_back({e, v});
8383
flags[v] &= ~invalidated;
84-
spfa_context::push(u, v);
84+
spfa_context::push(u, e, v);
8585
}
8686

8787
void invalidate_subtree(node_index v) {
@@ -91,9 +91,9 @@ namespace cp_algo::graph {
9191
to_invalidate.pop_back();
9292
flags[u] |= invalidated;
9393
flags[u] &= ~in_queue;
94-
for (auto dep: dependents[u]) {
95-
if (pre[dep].u == u) {
96-
to_invalidate.push_back(dep);
94+
for (auto [e, v]: dependents[u]) {
95+
if (pre[v] == e) {
96+
to_invalidate.push_back(v);
9797
}
9898
}
9999
dependents[u].clear();
@@ -105,16 +105,16 @@ namespace cp_algo::graph {
105105
Context sssp_impl(graph const& g, node_index s) {
106106
Context context(g.n());
107107
context.dist[s] = 0;
108-
context.push(s, s);
108+
context.push(s, -1, s);
109109
while(auto ov = context.next_node()) {
110110
node_index v = *ov;
111111
for(auto e: g.outgoing(v)) {
112-
node_index u = g.edge(e).to;
112+
node_index u = g.edge(e).traverse(v);
113113
auto w = g.edge(e).w;
114114
if(context.dist[v] + w < context.dist[u]) {
115115
context.dist[u] = context.dist[v] + w;
116-
context.pre[u] = {v, e};
117-
context.push(v, u);
116+
context.pre[u] = e;
117+
context.push(v, e, u);
118118
}
119119
}
120120
}
@@ -143,12 +143,12 @@ namespace cp_algo::graph {
143143
return negative_edges ? deep_spfa(g, s) : dijkstra(g, s);
144144
}
145145

146-
std::vector<edge_index> recover_path(auto const& pre, node_index s, node_index t) {
146+
std::vector<edge_index> recover_path(auto const& g, auto const& pre, node_index s, node_index t) {
147147
std::vector<edge_index> path;
148148
node_index v = t;
149149
while(v != s) {
150-
path.push_back(pre[v].e);
151-
v = pre[v].u;
150+
path.push_back(pre[v]);
151+
v = g.edge(pre[v]).traverse(v);
152152
}
153153
std::ranges::reverse(path);
154154
return path;
@@ -160,7 +160,7 @@ namespace cp_algo::graph {
160160
if (dist[t] == shortest_path_context::inf) {
161161
return std::nullopt;
162162
}
163-
return {{dist[t], recover_path(pre, s, t)}};
163+
return {{dist[t], recover_path(g, pre, s, t)}};
164164
}
165165
}
166166
#endif // CP_ALGO_GRAPH_SHORTEST_PATH_HPP

cp-algo/graph/tarjan.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@ namespace cp_algo::graph {
2626
}
2727

2828
void on_return_from_child(node_index v, edge_index e) {
29-
node_index u = base::g->edge(e).to;
29+
node_index u = base::g->edge(e).traverse(v);
3030
low[v] = std::min(low[v], low[u]);
3131
}
3232

3333
void on_back_edge(node_index v, edge_index e) {
34-
node_index u = base::g->edge(e).to;
34+
node_index u = base::g->edge(e).traverse(v);
3535
low[v] = std::min(low[v], tin[u]);
3636
}
3737

3838
void on_forward_cross_edge(node_index v, edge_index e) {
39-
node_index u = base::g->edge(e).to;
39+
node_index u = base::g->edge(e).traverse(v);
4040
low[v] = std::min(low[v], tin[u]);
4141
}
4242

@@ -83,7 +83,7 @@ namespace cp_algo::graph {
8383

8484
void on_return_from_child(node_index v, edge_index e) {
8585
base::on_return_from_child(v, e);
86-
node_index u = base::g->edge(e).to;
86+
node_index u = base::g->edge(e).traverse(v);
8787
if (base::low[u] >= base::tin[v]) {
8888
base::collect(u);
8989
base::components.push(v);

0 commit comments

Comments
 (0)