MimIR 0.1
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
link_cut_tree.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstddef>
4
5namespace mim::lct {
6
7/// This is an **intrusive** [Link-Cut-Tree](https://en.wikipedia.org/wiki/Link/cut_tree).
8/// Intrusive means that you have to inherit from this class via
9/// [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) like this:
10/// ```
11/// class Node : public lct::Node<Node, MyKey> {
12/// constexpr bool lt(const MyKey& key) const noexcept { /*...*/ }
13/// constexpr bool eq(const MyKey& key) const noexcept { /*...*/ }
14/// // ...
15/// };
16/// ```
17template<class P, class K> class Node {
18private:
19 P* self() { return static_cast<P*>(this); }
20 const P* self() const { return static_cast<const P*>(this); }
21
22public:
23 constexpr Node() noexcept = default;
24
25 ///@name Getters
26 ///@{
27 [[nodiscard]] bool contains(const K& k) noexcept {
28 expose();
29 for (auto n = this; n; n = n->self()->lt(k) ? n->bot : n->top)
30 if (n->self()->eq(k)) return n->splay(), true;
31
32 return false;
33 }
34
35 /// Find @p k or the element just greater than @p k.
36 constexpr P* find(const K& k) noexcept {
37 expose();
38 auto prev = this;
39 for (auto n = this; n;) {
40 if (n->self()->eq(k)) return n->splay(), n->self();
41
42 if (n->self()->lt(k)) {
43 n = n->bot;
44 } else {
45 prev = n;
46 n = n->top;
47 }
48 }
49
50 return prev->self();
51 }
52 ///@}
53
54 ///@name parent
55 ///@{
56 // clang-format off
57 constexpr Node* aux_parent() noexcept { return parent && (parent->bot == this || parent->top == this) ? parent : nullptr; }
58 constexpr P* path_parent() noexcept { return parent && (parent->bot != this && parent->top != this) ? parent->self() : nullptr; }
59 // clang-format on
60 ///@}
61
62 ///@name Splay Tree
63 ///@{
64
65 /// [Splays](https://hackmd.io/@CharlieChuang/By-UlEPFS#Operation1) `this` to the root of its splay tree.
66 constexpr void splay() noexcept {
67 while (auto p = aux_parent()) {
68 if (auto pp = p->aux_parent()) {
69 if (p->bot == this && pp->bot == p) { // zig-zig
70 pp->ror();
71 p->ror();
72 } else if (p->top == this && pp->top == p) { // zag-zag
73 pp->rol();
74 p->rol();
75 } else if (p->bot == this && pp->top == p) { // zig-zag
76 p->ror();
77 pp->rol();
78 } else { // zag-zig
79 assert(p->top == this && pp->bot == p);
80 p->rol();
81 pp->ror();
82 }
83 } else if (p->bot == this) { // zig
84 p->ror();
85 } else { // zag
86 assert(p->top == this);
87 p->rol();
88 }
89 }
90 }
91
92 /// Helpfer for Splay-Tree: rotate left/right:
93 /// ```
94 /// | Left | Right |
95 /// |-----------------------|------------------------|
96 /// | p p | p p |
97 /// | | | | | | |
98 /// | x c | x c |
99 /// | / \ -> / \ | / \ -> / \ |
100 /// | a c x d | c a d x |
101 /// | / \ / \ | / \ / \ |
102 /// | b d a b | d b b a |
103 /// ```
104 template<size_t l> constexpr void rotate() noexcept {
105 constexpr size_t r = (l + 1) % 2;
106 constexpr auto child = [](Node* n, size_t i) -> Node*& { return i == 0 ? n->bot : n->top; };
107
108 auto x = this;
109 auto p = x->parent;
110 auto c = child(x, r);
111 auto b = child(c, l);
112
113 if (b) b->parent = x;
114
115 if (p) {
116 if (child(p, l) == x) {
117 child(p, l) = c;
118 } else if (child(p, r) == x) {
119 child(p, r) = c;
120 } else {
121 /* only path parent */;
122 }
123 }
124
125 x->parent = c;
126 c->parent = p;
127 child(x, r) = b;
128 child(c, l) = x;
129 }
130
131 constexpr void rol() noexcept { return rotate<0>(); }
132 constexpr void ror() noexcept { return rotate<1>(); }
133 ///@}
134
135 /// @name Link-Cut-Tree
136 ///@{
137
138 /// Registers the edge `this -> child` in the *aux* tree.
139 constexpr void link(Node* child) noexcept {
140 this->expose();
141 child->expose();
142 if (!child->top) {
143 this->parent = child;
144 child->top = this;
145 }
146 }
147
148 /// Make a preferred path from `this` to root while putting `this` at the root of the *aux* tree.
149 /// @returns the last valid path_parent().
150 constexpr P* expose() noexcept {
151 Node* prev = nullptr;
152 for (auto curr = this; curr; prev = curr, curr = curr->parent) {
153 curr->splay();
154 assert(!prev || prev->parent == curr);
155 curr->bot = prev;
156 }
157 splay();
158 return prev->self();
159 }
160
161 /// Least Common Ancestor of `this` and @p other in the *aux* tree; leaves @p other expose%d.
162 /// @returns `nullptr`, if @p a and @p b are in different trees.
163 constexpr P* lca(Node* other) noexcept { return this->expose(), other->expose(); }
164
165 /// Is `this` a descendant of `other` in the *aux* tree?
166 /// Also `true`, if `this == other`.
167 constexpr bool is_descendant_of(Node* other) noexcept {
168 if (this == other) return true;
169 this->expose();
170 other->splay();
171 auto curr = this;
172 while (auto p = curr->aux_parent()) curr = p;
173 return curr == other;
174 }
175 ///@}
176
177 Node* parent = nullptr; ///< parent or path-parent
178 Node* bot = nullptr; ///< left/deeper/bottom/leaf-direction
179 Node* top = nullptr; ///< right/shallower/top/root-direction
180};
181
182} // namespace mim::lct
constexpr P * lca(Node *other) noexcept
Least Common Ancestor of this and other in the aux tree; leaves other exposed.
constexpr Node * aux_parent() noexcept
constexpr void link(Node *child) noexcept
Registers the edge this -> child in the aux tree.
constexpr void ror() noexcept
Node * bot
left/deeper/bottom/leaf-direction
constexpr P * expose() noexcept
Make a preferred path from this to root while putting this at the root of the aux tree.
constexpr bool is_descendant_of(Node *other) noexcept
Is this a descendant of other in the aux tree?
bool contains(const D *&k) noexcept
constexpr void rol() noexcept
constexpr void rotate() noexcept
Helpfer for Splay-Tree: rotate left/right:
constexpr P * path_parent() noexcept
constexpr P * find(const K &k) noexcept
Find k or the element just greater than k.
Node * top
right/shallower/top/root-direction
constexpr Node() noexcept=default
constexpr void splay() noexcept
Splays this to the root of its splay tree.