Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
titan_pylib documentation
titan_pylib documentation
  • ahc
    • ahc_settings
    • calc_ratio
    • optimizer
    • parallel_tester
  • algorithm
    • random
      • random
      • random_graph
      • random_tree
    • sort
      • bubble_sort
      • merge_sort
      • quick_sort
    • arithmetic_progression
    • cycle_memo
    • doubling
    • gray_code
    • mo
    • parser
    • permutation
  • data_structures
    • array
      • array_2D
      • csr_array
      • partial_persistent_array
      • persistent_array
    • avl_tree
      • avl_tree_dict
      • avl_tree_multiset
      • avl_tree_multiset2
      • avl_tree_multiset3
      • avl_tree_set
      • avl_tree_set2
      • avl_tree_set3
      • lazy_avl_tree
      • persistent_avl_tree_list
      • persistent_lazy_avl_tree
      • reversible_lazy_avl_tree
      • wbt_set
    • b_tree
      • b_tree_list
      • b_tree_set
    • binary_trie
      • binary_trie_multiset
      • binary_trie_set
    • bit_vector
      • avl_tree_bit_vector
      • bit_vector
      • bit_vector_interface
      • splay_tree_bit_vector
    • bst_base
      • bst_multiset_array_base
      • bst_multiset_node_base
      • bst_set_node_base
    • cumulative_sum
      • cumulative_op
      • cumulative_sum
      • cumulative_sum2D
    • deque
      • deque
      • foldable_deque
    • dict
      • hash_dict
    • dynamic_connectivity
      • euler_tour_tree
      • lazy_link_cut_tree
      • link_cut_tree
      • offline_dynamic_connectivity
    • fenwick_tree
      • dynamic_fenwick_tree
      • dynamic_fenwick_tree_2D
      • fenwick_tree
      • fenwick_tree_2D
      • fenwick_tree_2D_RAQ
      • fenwick_tree_RAQ
      • fenwick_tree_abst
    • heap
      • Randomized_meldable_heap
      • binomial_heap
      • deletable_max_heap
      • deletable_min_heap
      • double_ended_heap
      • max_heap
      • min_heap
      • radix_heap
    • list
      • linked_list
    • merge_sort_tree
      • merge_sort_tree
    • others
      • pair
    • rbst
      • lazy_rbst
    • red_black_tree
      • red_black_tree_multiset
      • red_black_tree_set
    • safe_hash
      • hash_counter
      • hash_defaultdict
      • hash_dict
      • hash_set
    • scapegoat_tree
      • scapegoat_tree_multiset
      • scapegoat_tree_set
    • segment_quadratic_division
      • quadratic_division_list
      • segment_lazy_quadratic_division
      • segment_quadratic_division
    • segment_tree
      • dual_commutative_segment_tree
      • dual_segment_tree
      • dual_segment_tree_commutative
      • dynamic_segment_tree
      • lazy_segment_tree
      • lazy_segment_tree_range
      • persistent_segment_tree
      • range_set_range_composite
      • segment_tree
      • segment_tree_RSQ
      • segment_tree_RmQ
      • segment_tree_interface
    • set
      • dynamic_fenwick_tree_multiset
      • dynamic_fenwick_tree_set
      • fenwick_tree_multiset
      • fenwick_tree_set
      • fenwick_tree_set_fast
      • hash_set
      • hashed_multiset
      • mex_multiset
      • min_max_multiset
      • min_max_set
      • multiset_quadratic_division
      • range_set
      • segki_set
      • set_quadratic_division
      • sorted_multiset
      • sorted_set
      • static_ordered_multiset
      • static_ordered_set
      • wordsize_tree_multiset
      • wordsize_tree_set
    • sparse_table
      • sparse_table
      • sparse_table_RmQ
    • splay_tree
      • lazy_splay_tree
      • lazy_splay_tree_array
      • reversible_lazy_splay_tree_array
      • splay_tree_dict
      • splay_tree_list
      • splay_tree_list_array
      • splay_tree_multiset
      • splay_tree_multiset2
      • splay_tree_multiset_sum
      • splay_tree_multiset_top_down
      • splay_tree_set
      • splay_tree_set_top_down
    • stack
      • foldable_stack
      • persistent_stack
      • stack
    • static_array_query
      • static_RmQ
      • static_range_mode_query
    • treap
      • treap_multiset
      • treap_set
    • union_find
      • fully_retroactive_union_find
      • partial_persistent_union_find
      • persistent_union_find
      • undoable_union_find
      • union_find
      • union_find_heavy
      • union_find_members
      • weighted_union_find
    • wavelet_matrix
      • cumulative_sum_wavelet_matrix
      • dynamic_wavelet_matrix
      • fenwick_tree_wavelet_matrix
      • wavelet_matrix
    • wbt
      • wbt_lazy_list
      • wbt_list
      • wbt_multiset
      • wbt_set
  • geometry
    • geometry
  • graph
    • flow
      • bipartite_max_matching
      • max_flow_dinic
      • max_flow_ford_fulkerson
    • hld
      • hld
      • hld_lazy_segment_tree
      • hld_noncommutative_segment_tree
      • hld_segment_tree
    • bellman_ford
    • bfs
    • cartesian_tree
    • dfs_tree
    • dijkstra
    • euler_tour
    • functional_graph
    • get_articulation_points
    • get_biconnected_components
    • get_bridge
    • get_scc
    • get_scc_graph
    • get_scc_lowlink
    • grid_bfs
    • is_bipartite_graph
    • lca
    • namori
    • perfect_binary_tree
    • rerooting_dp
    • rooted_tree
    • spfa
    • topological_sort
    • warshall_floyd
    • weighted_rooted_tree
  • io
    • fast_o
  • math
    • affine_map
    • big_int
    • comb
    • decimal_util
    • divisors
    • fraction
    • get_quotients
    • is_prime64
    • mod_array
    • mod_comb
    • mod_int
    • mod_int_1000000007
    • mod_int_998244353
    • mod_matrix
    • number
    • pollard_rho
  • string
    • dynamic_hash_string
    • get_suffix_array
    • hash_string
    • multi_hash_string
    • string_count
    • trie
Back to top
View this page

link_cut_tree¶

ソースコード¶

from titan_pylib.data_structures.dynamic_connectivity.link_cut_tree import LinkCutTree

view on github

展開済みコード¶

  1# from titan_pylib.data_structures.dynamic_connectivity.link_cut_tree import LinkCutTree
  2from array import array
  3
  4
  5class LinkCutTree:
  6    """LinkCutTree です。"""
  7
  8    # - link / cut / merge / split
  9    # - root / same
 10    # - lca / path_length / path_kth_elm
 11    # など
 12
 13    def __init__(self, n: int) -> None:
 14        self.n = n
 15        self.arr: array[int] = array("I", [self.n, self.n, self.n, 0] * (self.n + 1))
 16        # node.left  : arr[node<<2|0]
 17        # node.right : arr[node<<2|1]
 18        # node.par   : arr[node<<2|2]
 19        # node.rev   : arr[node<<2|3]
 20        self.size: array[int] = array("I", [1] * (self.n + 1))
 21        self.size[-1] = 0
 22        self.group_cnt = self.n
 23
 24    def _is_root(self, node: int) -> bool:
 25        return (self.arr[node << 2 | 2] == self.n) or not (
 26            self.arr[self.arr[node << 2 | 2] << 2] == node
 27            or self.arr[self.arr[node << 2 | 2] << 2 | 1] == node
 28        )
 29
 30    def _propagate(self, node: int) -> None:
 31        if node == self.n:
 32            return
 33        arr = self.arr
 34        if arr[node << 2 | 3]:
 35            arr[node << 2 | 3] = 0
 36            ln, rn = arr[node << 2], arr[node << 2 | 1]
 37            arr[node << 2] = rn
 38            arr[node << 2 | 1] = ln
 39            arr[ln << 2 | 3] ^= 1
 40            arr[rn << 2 | 3] ^= 1
 41
 42    def _update(self, node: int) -> None:
 43        if node == self.n:
 44            return
 45        ln, rn = self.arr[node << 2], self.arr[node << 2 | 1]
 46        self._propagate(ln)
 47        self._propagate(rn)
 48        self.size[node] = 1 + self.size[ln] + self.size[rn]
 49
 50    def _update_triple(self, x: int, y: int, z: int) -> None:
 51        self._propagate(self.arr[x << 2])
 52        self._propagate(self.arr[x << 2 | 1])
 53        self._propagate(self.arr[y << 2])
 54        self._propagate(self.arr[y << 2 | 1])
 55        self.size[z] = self.size[x]
 56        self.size[x] = 1 + self.size[self.arr[x << 2]] + self.size[self.arr[x << 2 | 1]]
 57        self.size[y] = 1 + self.size[self.arr[y << 2]] + self.size[self.arr[y << 2 | 1]]
 58
 59    def _update_double(self, x: int, y: int) -> None:
 60        self._propagate(self.arr[x << 2])
 61        self._propagate(self.arr[x << 2 | 1])
 62        self.size[y] = self.size[x]
 63        self.size[x] = 1 + self.size[self.arr[x << 2]] + self.size[self.arr[x << 2 | 1]]
 64
 65    def _splay(self, node: int) -> None:
 66        # splayを抜けた後、nodeは遅延伝播済みにするようにする
 67        # (splay後のnodeのleft,rightにアクセスしやすいと非常にラクなはず)
 68        if node == self.n:
 69            return
 70        _propagate, _is_root, _update_triple = (
 71            self._propagate,
 72            self._is_root,
 73            self._update_triple,
 74        )
 75        _propagate(node)
 76        if _is_root(node):
 77            return
 78        arr = self.arr
 79        pnode = arr[node << 2 | 2]
 80        while not _is_root(pnode):
 81            gnode = arr[pnode << 2 | 2]
 82            _propagate(gnode)
 83            _propagate(pnode)
 84            _propagate(node)
 85            f = arr[pnode << 2] == node
 86            g = (arr[gnode << 2 | f] == pnode) ^ (arr[pnode << 2 | f] == node)
 87            nnode = (node if g else pnode) << 2 | f ^ g
 88            arr[pnode << 2 | f ^ 1] = arr[node << 2 | f]
 89            arr[gnode << 2 | f ^ g ^ 1] = arr[nnode]
 90            arr[node << 2 | f] = pnode
 91            arr[nnode] = gnode
 92            arr[node << 2 | 2] = arr[gnode << 2 | 2]
 93            arr[gnode << 2 | 2] = nnode >> 2
 94            arr[arr[pnode << 2 | f ^ 1] << 2 | 2] = pnode
 95            arr[arr[gnode << 2 | f ^ g ^ 1] << 2 | 2] = gnode
 96            arr[pnode << 2 | 2] = node
 97            _update_triple(gnode, pnode, node)
 98            pnode = arr[node << 2 | 2]
 99            if arr[pnode << 2] == gnode:
100                arr[pnode << 2] = node
101            elif arr[pnode << 2 | 1] == gnode:
102                arr[pnode << 2 | 1] = node
103            else:
104                return
105        _propagate(pnode)
106        _propagate(node)
107        f = arr[pnode << 2] == node
108        arr[pnode << 2 | f ^ 1] = arr[node << 2 | f]
109        arr[node << 2 | f] = pnode
110        arr[arr[pnode << 2 | f ^ 1] << 2 | 2] = pnode
111        arr[node << 2 | 2] = arr[pnode << 2 | 2]
112        arr[pnode << 2 | 2] = node
113        self._update_double(pnode, node)
114
115    def expose(self, v: int) -> int:
116        """``v`` が属する木において、その木を管理しているsplay木の根から ``v`` までのパスを作ります。
117        償却 :math:`O(\\log{n})` です。
118        """
119        arr, n, _splay, _update = self.arr, self.n, self._splay, self._update
120        pre = v
121        while arr[v << 2 | 2] != n:
122            _splay(v)
123            arr[v << 2 | 1] = n
124            _update(v)
125            if arr[v << 2 | 2] == n:
126                break
127            pre = arr[v << 2 | 2]
128            _splay(pre)
129            arr[pre << 2 | 1] = v
130            _update(pre)
131        arr[v << 2 | 1] = n
132        _update(v)
133        return pre
134
135    def lca(self, u: int, v: int, root: int) -> int:
136        """``root`` を根としたときの、 ``u``, ``v`` の LCA を返します。
137        償却 :math:`O(\\log{n})` です。
138        """
139        self.evert(root)
140        self.expose(u)
141        return self.expose(v)
142
143    def link(self, c: int, p: int) -> None:
144        """辺 ``(c -> p)`` を追加します。
145        償却 :math:`O(\\log{n})` です。
146
147        制約:
148          ``c`` は元の木の根でなければならないです。
149        """
150        assert not self.same(c, p)
151        self.expose(c)
152        self.expose(p)
153        self.arr[c << 2 | 2] = p
154        self.arr[p << 2 | 1] = c
155        self._update(p)
156        self.group_cnt -= 1
157
158    def cut(self, c: int) -> None:
159        """辺 ``{c -> cの親}`` を削除します。
160        償却 :math:`O(\\log{n})` です。
161
162        制約:
163          ``c`` は元の木の根であってはいけないです。
164        """
165        arr = self.arr
166        self.expose(c)
167        assert arr[c << 2] != self.n
168        arr[arr[c << 2] << 2 | 2] = self.n
169        arr[c << 2] = self.n
170        self._update(c)
171        self.group_cnt += 1
172
173    def group_count(self) -> int:
174        """連結成分数を返します。
175        :math:`O(1)` です。
176        """
177        return self.group_cnt
178
179    def root(self, v: int) -> int:
180        """``v`` が属する木の根を返します。
181        償却 :math:`O(\\log{n})` です。
182        """
183        self.expose(v)
184        arr, n = self.arr, self.n
185        while arr[v << 2] != n:
186            v = arr[v << 2]
187            self._propagate(v)
188        self._splay(v)
189        return v
190
191    def same(self, u: int, v: int) -> bool:
192        """連結判定です。
193        償却 :math:`O(\\log{n})` です。
194
195        Returns:
196          bool: ``u``, ``v`` が同じ連結成分であれば ``True`` を、そうでなければ ``False`` を返します。
197        """
198        return self.root(u) == self.root(v)
199
200    def evert(self, v: int) -> None:
201        """``v`` を根にします。
202        償却 :math:`O(\\log{n})` です。
203        """
204        self.expose(v)
205        self.arr[v << 2 | 3] ^= 1
206        self._propagate(v)
207
208    def merge(self, u: int, v: int) -> bool:
209        """``u``, ``v`` が同じ連結成分なら ``False`` を返します。
210        そうでなければ辺 ``{u -> v}`` を追加して ``True`` を返します。
211        償却 :math:`O(\\log{n})` です。
212        """
213        if self.same(u, v):
214            return False
215        self.evert(u)
216        self.expose(v)
217        self.arr[u << 2 | 2] = v
218        self.arr[v << 2 | 1] = u
219        self._update(v)
220        self.group_cnt -= 1
221        return True
222
223    def split(self, u: int, v: int) -> bool:
224        """辺 ``{u -> v}`` があれば削除し ``True`` を返します。
225        そうでなければ何もせず ``False`` を返します。
226        償却 :math:`O(\\log{n})` です。
227        """
228        self.evert(u)
229        self.cut(v)
230        return True
231
232    def path_length(self, u: int, v: int) -> int:
233        """``u`` から ``v`` へのパスに含まれる頂点の数を返します。
234        存在しないときは ``-1`` を返します。
235        償却 :math:`O(\\log{n})` です。
236        """
237        if not self.same(u, v):
238            return -1
239        self.evert(u)
240        self.expose(v)
241        return self.size[v]
242
243    def path_kth_elm(self, s: int, t: int, k: int) -> int:
244        """``u`` から ``v`` へ ``k`` 個進んだ頂点を返します。
245        存在しないときは ``-1`` を返します。
246        償却 :math:`O(\\log{n})` です。
247        """
248        self.evert(s)
249        self.expose(t)
250        if self.size[t] <= k:
251            return -1
252        size, arr = self.size, self.arr
253        while True:
254            self._propagate(t)
255            s = size[arr[t << 2]]
256            if s == k:
257                self._splay(t)
258                return t
259            t = arr[t << 2 | (s < k)]
260            if s < k:
261                k -= s + 1
262
263    def __str__(self):
264        return f"{self.__class__.__name__}"
265
266    __repr__ = __str__

仕様¶

class LinkCutTree(n: int)[source]¶

Bases: object

LinkCutTree です。

cut(c: int) → None[source]¶

辺 {c -> cの親} を削除します。 償却 \(O(\log{n})\) です。

制約:

c は元の木の根であってはいけないです。

evert(v: int) → None[source]¶

v を根にします。 償却 \(O(\log{n})\) です。

expose(v: int) → int[source]¶

v が属する木において、その木を管理しているsplay木の根から v までのパスを作ります。 償却 \(O(\log{n})\) です。

group_count() → int[source]¶

連結成分数を返します。 \(O(1)\) です。

lca(u: int, v: int, root: int) → int[source]¶

root を根としたときの、 u, v の LCA を返します。 償却 \(O(\log{n})\) です。

link(c: int, p: int) → None[source]¶

辺 (c -> p) を追加します。 償却 \(O(\log{n})\) です。

制約:

c は元の木の根でなければならないです。

merge(u: int, v: int) → bool[source]¶

u, v が同じ連結成分なら False を返します。 そうでなければ辺 {u -> v} を追加して True を返します。 償却 \(O(\log{n})\) です。

path_kth_elm(s: int, t: int, k: int) → int[source]¶

u から v へ k 個進んだ頂点を返します。 存在しないときは -1 を返します。 償却 \(O(\log{n})\) です。

path_length(u: int, v: int) → int[source]¶

u から v へのパスに含まれる頂点の数を返します。 存在しないときは -1 を返します。 償却 \(O(\log{n})\) です。

root(v: int) → int[source]¶

v が属する木の根を返します。 償却 \(O(\log{n})\) です。

same(u: int, v: int) → bool[source]¶

連結判定です。 償却 \(O(\log{n})\) です。

Returns:

u, v が同じ連結成分であれば True を、そうでなければ False を返します。

Return type:

bool

split(u: int, v: int) → bool[source]¶

辺 {u -> v} があれば削除し True を返します。 そうでなければ何もせず False を返します。 償却 \(O(\log{n})\) です。

Next
offline_dynamic_connectivity
Previous
lazy_link_cut_tree
Copyright © 2024, titan
Made with Sphinx and @pradyunsg's Furo
On this page
  • link_cut_tree
    • ソースコード
      • 展開済みコード
    • 仕様
      • LinkCutTree
        • LinkCutTree.cut()
        • LinkCutTree.evert()
        • LinkCutTree.expose()
        • LinkCutTree.group_count()
        • LinkCutTree.lca()
        • LinkCutTree.link()
        • LinkCutTree.merge()
        • LinkCutTree.path_kth_elm()
        • LinkCutTree.path_length()
        • LinkCutTree.root()
        • LinkCutTree.same()
        • LinkCutTree.split()