Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_link_cut_tree_with_edges.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
61# ifndef TPL_LINK_CUT_TREE_WITH_EDGES_H
62# define TPL_LINK_CUT_TREE_WITH_EDGES_H
63
64# include <functional>
65# include <tpl_dynSetHash.H>
66# include <tpl_link_cut_tree.H>
67
68namespace Aleph
69{
77 template <typename VT = int,
78 typename ET = int,
80 class LazyTag = NoLazyTag<ET>>
83 {
85
86 public:
88 using Node = typename InternalLCT::Node;
89
90 private:
91 struct EdgeKey
92 {
95
96 [[nodiscard]] bool operator==(const EdgeKey &) const noexcept = default;
97 };
98
100 {
101 [[nodiscard]] size_t operator()(const EdgeKey & key) const noexcept
102 {
103 const size_t ha = std::hash<Node *>{}(key.a);
104 const size_t hb = std::hash<Node *>{}(key.b);
105 return ha ^ (hb + 0x9e3779b97f4a7c15ULL + (ha << 6) + (ha >> 2));
106 }
107 };
108
110 {
111 [[nodiscard]] bool operator()(const EdgeKey & lhs,
112 const EdgeKey & rhs) const noexcept
113 {
114 return lhs == rhs;
115 }
116 };
117
119 {
120 [[nodiscard]] bool operator()(Node *lhs, Node *rhs) const noexcept
121 {
122 return lhs == rhs;
123 }
124 };
125
126 [[nodiscard]] static size_t edge_key_hash(const EdgeKey & key) noexcept
127 {
128 return EdgeKeyHash{}(key);
129 }
130
131 [[nodiscard]] static size_t node_ptr_hash(Node *const & nd) noexcept
132 {
133 return std::hash<Node *>{}(nd);
134 }
135
136 struct EdgeRec
137 {
141 };
142
145 // Use the project hash map here so the edge index stays on Aleph containers.
152 size_t n_vertices_ = 0;
153 size_t n_components_ = 0;
154
155 [[nodiscard]] static EdgeKey make_edge_key(Node *u, Node *v) noexcept
156 {
157 return std::less<Node *>{}(u, v) ? EdgeKey{u, v} : EdgeKey{v, u};
158 }
159
160 size_t find_edge_idx(Node *u, Node *v) const noexcept
161 {
162 auto *it = edge_idx_.search(make_edge_key(u, v));
163 return it == nullptr ? edge_recs_.size() : it->second;
164 }
165
166 void remove_edge_at(size_t idx)
167 {
168 const auto removed = edge_recs_(idx);
170
171 if (idx + 1 < edge_recs_.size())
172 {
173 edge_recs_(idx) = edge_recs_(edge_recs_.size() - 1);
174 const auto & moved = edge_recs_(idx);
175 edge_idx_.find(make_edge_key(moved.u, moved.v)) = idx;
176 }
177 edge_recs_.remove_last();
178 }
179
181 {
182 ah_invalid_argument_if(nd == nullptr) << "null handle";
183 auto *entry = vertex_vals_.search(nd);
184 ah_domain_error_if(entry == nullptr) << "vertex does not exist";
185 return entry->second;
186 }
187
188 void require_vertex(Node * x) const
189 {
190 ah_invalid_argument_if(x == nullptr) << "null handle";
191 ah_domain_error_if(vertex_vals_.search(x) == nullptr)
192 << "handle does not belong to this tree";
193 }
194
195 public:
198
201
204
207
210
220 Node * make_vertex(const VT & val = VT{})
221 {
222 auto *nd = lct_.make_vertex(EdgeMonoid::identity());
223 try
224 {
225 vertex_vals_.insert(nd, val);
226 }
227 catch (...)
228 {
230 throw;
231 }
232 ++n_vertices_;
234 return nd;
235 }
236
238 [[nodiscard]] size_t size() const noexcept { return n_vertices_; }
239
242 {
243 return n_components_;
244 }
245
248 {
249 return edge_recs_.size();
250 }
251
257 [[nodiscard]] const VT &get_vertex_val(Node *x) const
258 {
259 return lookup_vertex_val(x);
260 }
261
267 void set_vertex_val(Node *x, const VT & val)
268 {
270 vertex_vals_.find(x) = val;
271 }
272
278 [[nodiscard]] bool connected(Node *u, Node *v)
279 {
282 return lct_.connected(u, v);
283 }
284
296 void link(Node *u, Node *v, const ET & weight)
297 {
300 ah_invalid_argument_if(u == v) << "self-loop";
301 ah_domain_error_if(lct_.connected(u, v)) << "already connected";
302
303 const size_t idx = edge_recs_.size();
304 const auto key = make_edge_key(u, v);
305
306 edge_recs_.reserve(idx + 1);
307 edge_recs_.append({u, v, nullptr});
308
309 Node *e = nullptr;
310 bool linked_u = false;
311 bool linked_v = false;
312
313 try
314 {
315 auto *entry = edge_idx_.insert(key, idx);
316 ah_domain_error_if(entry == nullptr) << "edge already exists";
317
318 e = lct_.make_vertex(weight);
319 lct_.link(u, e);
320 linked_u = true;
321 lct_.link(e, v);
322 linked_v = true;
323 edge_recs_(idx).edge_node = e;
324 }
325 catch (...)
326 {
327 if (linked_v)
328 lct_.cut(e, v);
329 if (linked_u)
330 lct_.cut(u, e);
331 if (e != nullptr)
333 edge_recs_.remove_last();
334 if (edge_idx_.contains(key))
335 edge_idx_.remove(key);
336 throw;
337 }
338
340 }
341
351 void cut(Node *u, Node *v)
352 {
355 size_t idx = find_edge_idx(u, v);
356 ah_domain_error_if(idx == edge_recs_.size()) << "edge does not exist";
357
358 Node *e = edge_recs_(idx).edge_node;
359 lct_.cut(u, e);
360 lct_.cut(e, v);
361 remove_edge_at(idx);
364 }
365
375 {
378 return lct_.path_query(u, v);
379 }
380
387 {
389 lct_.make_root(x);
390 }
391
401 {
403 return lct_.find_root(x);
404 }
405
415 [[nodiscard]] size_t path_size(Node *u, Node *v)
416 {
419 const size_t internal_size = lct_.path_size(u, v);
420 return (internal_size + 1) / 2;
421 }
422
428 void set_edge_val(Node *u, Node *v, const ET & new_weight)
429 {
432 size_t idx = find_edge_idx(u, v);
433 ah_domain_error_if(idx == edge_recs_.size()) << "edge does not exist";
434
435 lct_.set_val(edge_recs_(idx).edge_node, new_weight);
436 }
437
443 [[nodiscard]] const ET &get_edge_val(Node *u, Node *v)
444 {
447 size_t idx = find_edge_idx(u, v);
448 ah_domain_error_if(idx == edge_recs_.size()) << "edge does not exist";
449
450 return lct_.get_val(edge_recs_(idx).edge_node);
451 }
452
453 // ---- snapshot export ----
454
465
486 {
489
491
492 // Build adjacency from edge records (only edges in root's component)
493 // adj[i] = list of (neighbor_vertex_index, edge_weight)
494 struct AdjEntry
495 {
496 size_t neighbor;
497 ET weight;
498 };
499
500 // Collect vertices in root's component; use a hash map for O(1) lookup
504 auto add_vert = [&](Node * nd) {
505 if (node_to_idx.search(nd) == nullptr)
506 {
507 node_to_idx.insert(nd, verts.size());
508 verts.append(nd);
509 }
510 };
511 add_vert(root);
512 for (size_t i = 0; i < edge_recs_.size(); ++i)
513 if (const auto & er = edge_recs_(i); lct_.connected(er.u, root))
514 {
515 add_vert(er.u);
516 add_vert(er.v);
517 }
518
519 // Build adjacency lists
520 size_t nv = verts.size();
522 for (size_t i = 0; i < nv; ++i)
523 adj.append(Array<AdjEntry>());
524
525 for (size_t i = 0; i < edge_recs_.size(); ++i)
526 {
527 const auto & er = edge_recs_(i);
528 auto * up = node_to_idx.search(er.u);
529 auto * vp = node_to_idx.search(er.v);
530 if (up != nullptr and vp != nullptr)
531 {
532 ET w = lct_.get_val(er.edge_node);
533 adj(up->second).append({vp->second, w});
534 adj(vp->second).append({up->second, w});
535 }
536 }
537
538 // BFS from root to build Tree_Node tree
540 tn.putn(nv);
541 for (size_t i = 0; i < nv; ++i)
542 tn(i) = nullptr;
543
544 size_t root_idx = node_to_idx.search(root)->second;
545
546 // RAII guard: calls destroy_tree on the root TN if an exception escapes.
547 // Each new TN is linked to its parent immediately after allocation, so
548 // destroy_tree(root) reaches all allocated nodes on any exception path.
549 struct ScopedTreeOwner
550 {
551 TN * root = nullptr;
552 ~ScopedTreeOwner() noexcept { if (root != nullptr) destroy_tree(root); }
553 TN * release() noexcept { TN * r = root; root = nullptr; return r; }
554 } guard;
555
556 guard.root = new TN({root, lookup_vertex_val(root), EdgeMonoid::identity()});
557 tn(root_idx) = guard.root;
558
559 // BFS queue (simple array-based)
560 Array<size_t> queue;
561 queue.append(root_idx);
562 size_t qfront = 0;
563
564 while (qfront < queue.size())
565 {
566 size_t ci = queue(qfront++);
567 for (size_t j = 0; j < adj(ci).size(); ++j)
568 if (auto [ni, w] = adj(ci)(j); tn(ni) == nullptr)
569 {
570 tn(ni) = new TN({verts(ni), lookup_vertex_val(verts(ni)), w});
571 tn(ci)->insert_rightmost_child(tn(ni));
572 queue.append(ni);
573 }
574 }
575
576 return guard.release();
577 }
578 };
579
581 template <typename EdgeT = int, class Monoid = DefaultMonoid<EdgeT>>
583} // namespace Aleph
584
585# endif /* TPL_LINK_CUT_TREE_WITH_EDGES_H */
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:522
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
void destroy_tree(Tree &tree)
Definition avl-rb-rk.cc:520
long double w
Definition btreepic.C:153
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:139
constexpr size_t size() const noexcept
Return the number of elements stored in the stack.
Definition tpl_array.H:351
T & append(const T &data)
Append a copy of data
Definition tpl_array.H:245
Pair * insert(const Key &key, const Data &data)
Inserts into the hash map the pair (key, record) indexed by key.
Pair * search(const Key &key) const noexcept
Searches for key and, if found, returns a pointer to the associated pair stored in the table.
Generic m-ary trees.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
const unsigned long DefaultPrime
Default prime number used when no specific size is requested.
Definition primes.C:381
Default (no-op) monoid for connectivity-only usage.
No-op lazy tag (default — no deferred path updates).
gsl_rng * r
Dynamic set implementations based on hash tables.