{-# LANGUAGE DerivingVia #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeFamilies #-} -- | Link/cut tree: dynamic forest with monoid values on vertices. If you need to have monoid values -- on edges, treat the original edges as new vertices. -- -- - __Most operations are unsafe__; user must ensure connectivities of \(u\) and \(v\) before -- running each query. -- - This specific implementation is not capable of applying monoid action to a subtree. -- -- ==== __Example__ -- -- Create a link/cut tree of monoid @Sum Int@ with inverse operator `negate`: -- -- >>> import AtCoder.Extra.Tree.Lct qualified as Lct -- >>> import Data.Semigroup (Sum (..)) -- >>> import Data.Vector.Unboxed qualified as VU -- >>> -- 0--1--2 -- >>> -- +--3 -- >>> lct <- Lct.buildInv negate (VU.generate 4 Sum) $ VU.fromList [(0, 1), (1, 2), (1, 3)] -- -- ===== `prodPath`, `prodSubtree`, `prodTree` -- -- Monoid products can be calculated for paths or subtrees: -- -- >>> Lct.prodPath lct 0 2 -- Sum {getSum = 3} -- -- >>> -- If we create the LCT with `buildInv`, we can use `prodSubtree`: -- >>> Lct.prodSubtree lct 1 {- parent -} 2 -- Sum {getSum = 4} -- -- `root` returns the current root vertex of the underlying tree, which is not easy to predict: -- -- >>> Lct.root lct 3 -- 2 -- -- ===== `lca`, `jump`, `lengthBetween` -- -- Set (`evert`) the root of the underlying tree to \(0\) and get the `lca` of vertices \(2\) and -- \(3\): -- -- >>> Lct.evert lct 0 -- >>> Lct.lca lct 2 3 -- 1 -- -- Similar to @Hld@, `Lct` allows various tree queries: -- -- >>> Lct.jump lct {- path -} 2 3 {- k -} 2 -- 3 -- -- >>> Lct.jumpMaybe lct {- path -} 2 3 {- k -} 1000 -- Nothing -- -- >>> Lct.lengthBetween lct {- path -} 2 3 -- 2 -- -- ===== `parent` -- -- >>> Lct.evert lct 0 -- set root `0` -- >>> Lct.parent lct 0 -- under root `0`, parent of `0` is `Nothing`: -- Nothing -- -- >>> Lct.evert lct 0 -- set root `0` -- >>> Lct.parent lct 1 -- under root `0`, parent of `1` is `0`: -- Just 0 -- -- ===== `link` / `cut` -- -- Edges can be dynamically added (`link`) or removed (`cut`): -- -- >>> -- 0 1 2 -- >>> -- +--3 -- >>> Lct.cut lct 0 1 -- >>> Lct.cut lct 1 2 -- >>> VU.generateM 4 (Lct.root lct) -- [0,1,2,1] -- -- >>> -- +-----+ -- >>> -- 0 1 2 -- >>> -- +--3 -- >>> Lct.link lct 0 2 -- >>> VU.generateM 4 (Lct.root lct) -- [2,1,2,1] -- -- @since 1.1.1.0 module AtCoder.Extra.Tree.Lct ( -- Link/cut tree Lct (..), Vertex, -- * Constructors new, newInv, build, buildInv, -- * Monoid value access read, write, modify, modifyM, -- * Tree operations -- ** Link/cut link, cut, -- ** Evert/expose evert, expose, expose_, -- * Tree queries root, same, parent, jump, jumpMaybe, lengthBetween, lca, lcaMaybe, -- ** Monoid products prodPath, prodSubtree, prodTree, ) where import AtCoder.Internal.Assert qualified as ACIA import Control.Monad (unless, when) import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim) import Control.Monad.ST (ST) import Data.Bit import Data.Bits import Data.Maybe import Data.Vector.Generic.Mutable qualified as VGM import Data.Vector.Unboxed qualified as VU import Data.Vector.Unboxed.Mutable qualified as VUM import GHC.Stack (HasCallStack) import Prelude hiding (read) -- import GHC.Stack (HasCallStack) -- | Alias of vertex type. type Vertex = Int {-# INLINE undefLct #-} undefLct :: Vertex undefLct = -1 {-# INLINE nullLct #-} nullLct :: Vertex -> Bool nullLct = (== -1) -- We could optimize the with options, but -- | Link/cut tree. -- -- @since 1.1.1.0 data Lct s a = Lct { -- | The number of vertices. -- -- @since 1.1.1.0 nLct :: {-# UNPACK #-} !Int, -- | Decomposed node data storage: left children. -- -- @since 1.1.1.0 lLct :: !(VUM.MVector s Vertex), -- | Decomposed node data storage: right children. -- -- @since 1.1.1.0 rLct :: !(VUM.MVector s Vertex), -- | Decomposed node data storage: parents. -- -- @since 1.1.1.0 pLct :: !(VUM.MVector s Vertex), -- | Decomposed node data storage: subtree sizes. -- -- @since 1.1.1.0 sLct :: !(VUM.MVector s Int), -- | Decomposed node data storage: reverse flags. -- -- @since 1.1.1.0 revLct :: !(VUM.MVector s Bit), -- | Decomposed node data storage: monoid values. -- -- @since 1.1.1.0 vLct :: !(VUM.MVector s a), -- | Decomposed node data storage: monoid products. -- -- @since 1.1.1.0 prodLct :: !(VUM.MVector s a), -- | Decomposed node data storage: dual monoid products (right foldings). This is required for -- non-commutative monoids only. -- -- @since 1.1.1.0 dualProdLct :: !(VUM.MVector s a), -- | Decomposed node data storage: path-parent monoid products. This works for subtree product -- queries over commutative monoids only. -- -- @since 1.1.1.0 midLct :: !(VUM.MVector s a), -- | Decomposed node data storage: monoid product of subtree. This works for subtree product -- queries over commutative monoids only. -- -- @since 1.1.1.0 subtreeProdLct :: !(VUM.MVector s a), -- | Inverse operator of the monoid. This works for subtree product queries over commutative -- monoids only. -- -- @since 1.1.1.0 invOpLct :: !(a -> a) } -- | \(O(n)\) Creates a link/cut tree with \(n\) vertices and no edges. This setup disables subtree -- queries (`prodSubtree`). -- -- @since 1.1.1.0 {-# INLINE new #-} new :: (PrimMonad m, Monoid a, VU.Unbox a) => Int -> m (Lct (PrimState m) a) new = newInv id -- | \(O(n)\) Creates a link/cut tree with an inverse operator, initial monoid values and no edges. -- This setup enables subtree queries (`prodSubtree`). -- -- @since 1.1.1.0 {-# INLINE newInv #-} newInv :: (PrimMonad m, Monoid a, VU.Unbox a) => (a -> a) -> Int -> m (Lct (PrimState m) a) newInv !invOpLct nLct = buildInv invOpLct (VU.replicate nLct mempty) VU.empty -- | \(O(n + m \log n)\) Creates a link/cut tree of initial monoid values and initial edges. This -- setup disables subtree queries (`prodSubtree`). -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- -- @since 1.1.1.0 {-# INLINE build #-} build :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => -- | Vertex monoid values VU.Vector a -> -- | Edges VU.Vector (Vertex, Vertex) -> -- | Link/cut tree m (Lct (PrimState m) a) build xs es = stToPrim $ buildInv id xs es -- | \(O(n + m \log n)\) Creates a link/cut tree with an inverse operator, initial monoid values and -- initial edges. This setup enables subtree queries (`prodSubtree`). -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- -- @since 1.1.1.0 {-# INLINE buildInv #-} buildInv :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => -- | Inverse operator (a -> a) -> -- | Vertex monoid values VU.Vector a -> -- | Edges VU.Vector (Vertex, Vertex) -> -- | Link/cut tree m (Lct (PrimState m) a) buildInv invOpLct xs es = stToPrim $ buildST invOpLct xs es -- ------------------------------------------------------------------------------------------------- -- Monoid value access -- ------------------------------------------------------------------------------------------------- -- | \(O(1)\). Reads the monoid value on a vertex \(v\). -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.5.1.0 {-# INLINE read #-} read :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> m a read lct v = stToPrim $ do VGM.read (vLct lct) v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.read" v (nLct lct) -- | Amortized \(O(\log n)\). Writes to the monoid value of a vertex \(v\). -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE write #-} write :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> a -> m () write lct v x = stToPrim $ do -- make @v@ a new root of the underlying tree: evertST lct v VGM.unsafeWrite (vLct lct) v x where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.write" v (nLct lct) -- | Amortized \(O(\log n)\). Given a user function \(f\), modifies the monoid value of a vertex -- \(v\). -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE modify #-} modify :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> (a -> a) -> Vertex -> m () modify lct f v = stToPrim $ do -- make @v@ a new root of the underlying tree: evertST lct v VGM.unsafeModify (vLct lct) f v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.modify" v (nLct lct) -- | Amortized \(O(\log n)\). Given a user function \(f\), modifies the monoid value of a vertex -- \(v\). -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE modifyM #-} modifyM :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> (a -> m a) -> Vertex -> m () modifyM lct f v = do -- make @v@ a new root of the underlying tree: stToPrim $ evertST lct v VGM.unsafeModifyM (vLct lct) f v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.modifyM" v (nLct lct) -- ------------------------------------------------------------------------------------------------- -- Link/cut operations -- ------------------------------------------------------------------------------------------------- -- | Amortized \(O(\log n)\). Creates an edge between \(c\) and \(p\). In the represented tree, the -- \(p\) will be the parent of \(c\). -- -- ==== Constraints -- - \(0 \le c, p \lt n\) -- - \(u \ne v\) -- - \(c\) and \(p\) are in different trees, otherwise the behavior is undefined. -- -- @since 1.1.1.0 {-# INLINE link #-} link :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m () link lct c p = stToPrim $ linkST lct c p where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.link" c (nLct lct) !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.link" p (nLct lct) -- | Amortized \(O(\log n)\). Deletes an edge between \(u\) and \(v\). -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- - \(u \ne v\) -- - There's an edge between \(u\) and \(v\), otherwise the behavior is undefined. -- -- @since 1.1.1.0 {-# INLINE cut #-} cut :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m () cut lct u v = stToPrim $ cutST lct u v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.cut" u (nLct lct) !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.cut" v (nLct lct) -- | Amortized \(O(\log n)\). Makes \(v\) a new root of the underlying tree. -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE evert #-} evert :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> m () evert lct v = stToPrim $ evertST lct v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.evert" v (nLct lct) -- | Amortized \(O(\log n)\). Makes \(v\) and the root to be in the same preferred path (auxiliary -- tree). After the operation, \(v\) will be the new root and all the children will be detached from -- the preferred path. -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE expose #-} expose :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> m Vertex expose lct v = stToPrim $ exposeST lct v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.expose" v (nLct lct) -- | Amortized \(O(\log n)\). `expose` with the return value discarded. -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE expose_ #-} expose_ :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> m () expose_ lct v0 = stToPrim $ do _ <- exposeST lct v0 pure () -- ------------------------------------------------------------------------------------------------- -- Jump, LCA -- ------------------------------------------------------------------------------------------------- -- | \(O(\log n)\) Returns the root of the underlying tree. -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE root #-} root :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> m Vertex root lct c0 = stToPrim $ rootST lct c0 -- | \(O(\log n)\) Returns whether the vertices \(u\) and \(v\) are in the same connected component -- (have the same `root`). -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- -- @since 1.5.1.0 {-# INLINE same #-} same :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m Bool same lct u v = stToPrim $ do r1 <- rootST lct u r2 <- rootST lct v pure $ r1 == r2 -- | \(O(\log n)\) Returns the parent vertex in the underlying tree. -- -- ==== Constraints -- - \(0 \le v \lt n\) -- -- @since 1.1.1.0 {-# INLINE parent #-} parent :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> m (Maybe Vertex) parent lct v = stToPrim $ parentST lct v -- | \(O(\log n)\) Given a path between \(u\) and \(v\), returns the \(k\)-th vertex from \(u\) in -- the path. -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- - \(0 \le k \lt \mathrm{|path|}\) -- - \(u\) and \(v\) must be in the same connected component, otherwise the behavior is undefined. -- -- @since 1.1.1.0 {-# INLINE jump #-} jump :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> Int -> m Vertex jump lct u v k = stToPrim $ do fromMaybe (error "AtCoder.Extra.Tree.Lct.jump: invalid jump") <$> jumpMaybeST lct u v k -- | \(O(\log n)\) Given a path between \(u\) and \(v\), returns the \(k\)-th vertex from \(u\) in -- the path. -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- - \(u\) and \(v\) must be in the same connected component, otherwise the behavior is undefined. -- -- @since 1.5.1.0 {-# INLINE jumpMaybe #-} jumpMaybe :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> Int -> m (Maybe Vertex) jumpMaybe lct u v k = stToPrim $ jumpMaybeST lct u v k -- | \(O(\log n)\) Returns the length of path between \(u\) and \(v\). -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- - \(u\) and \(v\) must be in the same connected component. -- -- @since 1.5.1.0 {-# INLINE lengthBetween #-} lengthBetween :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m Vertex lengthBetween lct u v = stToPrim $ do lengthBetweenST lct u v -- | \(O(\log n)\) Returns the LCA of \(u\) and \(v\). Because the root of the underlying tree changes -- in almost every operation, one might want to use `evert` beforehand. -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- - \(u\) and \(v\) must be in the same connected component. -- -- @since 1.1.1.0 {-# INLINE lca #-} lca :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m Vertex lca lct u v = stToPrim $ do fromMaybe (error ("AtCoder.Extra.Tree.Lct.lca: given two vertices in different connected components " ++ show (u, v))) <$> lcaMaybe lct u v -- | \(O(\log n)\) Returns the LCA of \(u\) and \(v\). Because the root of the underlying tree changes -- in almost every operation, one might want to use `evert` beforehand. -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- -- @since 1.5.1.0 {-# INLINE lcaMaybe #-} lcaMaybe :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m (Maybe Vertex) lcaMaybe lct u v = stToPrim $ do ru <- rootST lct u rv <- rootST lct v if ru == rv then do _ <- exposeST lct u Just <$> exposeST lct v else pure Nothing -- ------------------------------------------------------------------------------------------------- -- Monoid product -- ------------------------------------------------------------------------------------------------- -- | Amortized \(O(\log n)\). Folds a path between \(u\) and \(v\) (inclusive). -- -- ==== Constraints -- - \(0 \le u, v \lt n\) -- - \(u\) and \(v\) must be in the same connected component, otherwise the return value is -- nonsense. -- -- @since 1.1.1.0 {-# INLINE prodPath #-} prodPath :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => Lct (PrimState m) a -> Vertex -> Vertex -> m a prodPath lct@Lct {prodLct} u v = stToPrim $ do -- make @u@ the root of the underlying tree evertST lct u -- make @v@ in the same preferred path as @u@ _ <- exposeST lct v -- now that @v@ is at the root of the auxiliary tree, its aggregation value is the path folding: VGM.unsafeRead prodLct v where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.prodPath" u (nLct lct) !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.prodPath" v (nLct lct) -- | Amortized \(O(\log n)\). Fold the subtree under \(v\), considering \(p\) as the root-side -- vertex. Or, if \(p\) equals \(v\), \(v\) will be the new root. -- -- ==== Constraints -- - The inverse operator must be set on construction (`newInv` or `buildInv`). -- - \(0 \le u, v \lt n\) -- -- @since 1.1.1.0 {-# INLINE prodSubtree #-} prodSubtree :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => -- | Link/cut tree Lct (PrimState m) a -> -- | Vertex \(v\) Vertex -> -- | Parent \(p\) (not need be adjacent to \(v\)), or same as \(v\), making it a new root. Vertex -> -- | Subtree's monoid product m a prodSubtree lct v rootOrParent = stToPrim $ prodSubtreeST lct v rootOrParent -- | Amortized \(O(\log n)\). Fold a tree that contains \(v\). -- -- ==== Constraints -- - The inverse operator must be set on construction (`newInv` or `buildInv`). -- - \(0 \le v \lt n\) -- -- @since 1.5.1.0 {-# INLINE prodTree #-} prodTree :: (HasCallStack, PrimMonad m, Monoid a, VU.Unbox a) => -- | Link/cut tree Lct (PrimState m) a -> -- | Vertex \(v\) Vertex -> -- | Subtree's monoid product m a prodTree lct v = stToPrim $ prodSubtreeST lct v v -- ------------------------------------------------------------------------------------------------- -- Internal -- ------------------------------------------------------------------------------------------------- {-# INLINEABLE buildST #-} buildST :: (HasCallStack, Monoid a, VU.Unbox a) => -- | Inverse operator (a -> a) -> -- | Vertex monoid values VU.Vector a -> -- | Edges VU.Vector (Vertex, Vertex) -> -- | Link/cut tree ST s (Lct s a) buildST invOpLct xs es = do lct <- do let !nLct = VU.length xs lLct <- VUM.replicate nLct undefLct rLct <- VUM.replicate nLct undefLct pLct <- VUM.replicate nLct undefLct sLct <- VUM.replicate nLct 0 revLct <- VUM.replicate nLct (Bit False) vLct <- VU.thaw xs prodLct <- VUM.replicate nLct mempty dualProdLct <- VUM.replicate nLct mempty midLct <- VUM.replicate nLct mempty subtreeProdLct <- VUM.replicate nLct mempty pure Lct {..} VU.forM_ es $ \(!u, !v) -> do link lct u v pure lct -- * Balancing -- | \(O(1)\) Rotates up a non-root node. {-# INLINEABLE rotateST #-} rotateST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> ST s () rotateST lct@Lct {pLct, lLct, rLct} v = do p <- VGM.unsafeRead pLct v pp <- VGM.unsafeRead pLct p pl <- VGM.unsafeRead lLct p c <- if pl == v then do -- rotate right: -- p v <-- reference from `pp` is updated later -- / \ -- v -> p -- \ / -- c c c <- VGM.unsafeExchange rLct v p VGM.unsafeWrite lLct p c pure c else do -- rotate left: -- p v <-- reference from `pp` is updated later -- \ / -- v -> p -- / \ -- c c c <- VGM.unsafeExchange lLct v p VGM.unsafeWrite rLct p c pure c updateNodeST lct p updateNodeST lct v -- update the reference from `pp`: unless (nullLct pp) $ do ppl <- VGM.unsafeRead lLct pp if ppl == p then VGM.unsafeWrite lLct pp v else do ppr <- VGM.unsafeRead rLct pp if ppr == p then VGM.unsafeWrite rLct pp v else do -- overwrite the light (path-parent) pointer: changeLightST lct pp p v -- update parent pointers to `pp`: pp <-- v <-- p <-- c VGM.unsafeWrite pLct v pp VGM.unsafeWrite pLct p v unless (nullLct c) $ do VGM.unsafeWrite pLct c p -- | Amortized \(O(\log n)\). Moves a node up to the root, performing self-balancing heuristic -- called rotations. {-# INLINEABLE splayST #-} splayST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> ST s () splayST lct@Lct {pLct} c = do pushNodeST lct c let inner = do isRootC <- isRootNodeST lct c unless isRootC $ do p <- VGM.unsafeRead pLct c pp <- if nullLct p then pure undefLct else VGM.unsafeRead pLct p placeP <- nodePlaceST lct p if placeP == RootNodeLct then do pushNodeST lct p pushNodeST lct c rotateST lct c else do placeC <- nodePlaceST lct c pushNodeST lct pp pushNodeST lct p pushNodeST lct c if placeC == placeP then do -- Rotate right twice: -- -- pp p c -- / / \ \ -- p -> c pp -> p -- / \ -- c pp -- Or rotate left twice: -- -- pp p c -- \ / \ / -- p -> pp c -> p -- \ / -- c pp rotateST lct p rotateST lct c else do -- pp pp c -- / / | \ -- p -> c -> p pp -- \ / -- c p rotateST lct c rotateST lct c inner inner -- * Node helpers -- | \(O(1)\) {-# INLINE isRootNodeST #-} isRootNodeST :: Lct s a -> Vertex -> ST s Bool isRootNodeST lct v = do (== RootNodeLct) <$> nodePlaceST lct v -- TODO: return heavy/light notion data NodePlaceLct = RootNodeLct | LeftNodeLct | RightNodeLct deriving (Eq) -- | \(O(1)\) {-# INLINEABLE nodePlaceST #-} nodePlaceST :: Lct s a -> Vertex -> ST s NodePlaceLct nodePlaceST Lct {lLct, rLct, pLct} v = do p <- VGM.unsafeRead pLct v if nullLct p then pure RootNodeLct else do pl <- VGM.unsafeRead lLct p if pl == v then pure LeftNodeLct else do pr <- VGM.unsafeRead rLct p if pr == v then pure RightNodeLct else pure RootNodeLct -- ------------------------------------------------------------------------------------------------- -- Node operations -- ------------------------------------------------------------------------------------------------- -- | \(O(1)\) Propagates the lazily propagated values on a node. {-# INLINEABLE pushNodeST #-} pushNodeST :: (VU.Unbox a) => Lct s a -> Vertex -> ST s () pushNodeST lct@Lct {lLct, rLct, revLct} v = do Bit b <- VGM.unsafeExchange revLct v (Bit False) when b $ do l <- VGM.unsafeRead lLct v r <- VGM.unsafeRead rLct v unless (nullLct l) $ reverseNodeST lct l unless (nullLct r) $ reverseNodeST lct r -- | \(O(1)\) {-# INLINEABLE reverseNodeST #-} reverseNodeST :: (VU.Unbox a) => Lct s a -> Vertex -> ST s () reverseNodeST lct@Lct {revLct} i = do -- lazily propagate new reverse from the children, or cancel: VGM.unsafeModify revLct (xor (Bit True)) i -- swap swapLrNodeST lct i -- | \(O(1)\) Reverses the left and the right children, lazily and recursively. {-# INLINEABLE swapLrNodeST #-} swapLrNodeST :: (VU.Unbox a) => Lct s a -> Vertex -> ST s () swapLrNodeST Lct {lLct, rLct, prodLct, dualProdLct} i = do -- swap children VGM.unsafeModifyM lLct (VGM.unsafeExchange rLct i) i -- swap prodLct[i] and dualProdLct[i] VGM.unsafeModifyM prodLct (VGM.unsafeExchange dualProdLct i) i -- | \(O(1)\) Recomputes the node size and the monoid product. {-# INLINEABLE updateNodeST #-} updateNodeST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> ST s () updateNodeST Lct {..} i = do l <- VGM.unsafeRead lLct i r <- VGM.unsafeRead rLct i v <- VGM.unsafeRead vLct i m <- VGM.unsafeRead midLct i (!size', !prod', !dualProd', !subtreeProd') <- if nullLct l then pure (1 :: Int, v, v, v <> m) else do lSize <- VGM.unsafeRead sLct l lProd <- VGM.unsafeRead prodLct l lDualProd <- VGM.unsafeRead dualProdLct l lSubtreeProd <- VGM.unsafeRead subtreeProdLct l pure (lSize + 1, lProd <> v, v <> lDualProd, lSubtreeProd <> v <> m) (!size'', !prod'', !dualProd'', !subtreeProd'') <- if nullLct r then pure (size', prod', dualProd', subtreeProd') else do rSize <- VGM.unsafeRead sLct r rProd <- VGM.unsafeRead prodLct r rDualProd <- VGM.unsafeRead dualProdLct r rSubtreeProd <- VGM.unsafeRead subtreeProdLct r pure (size' + rSize, prod' <> rProd, rDualProd <> dualProd', subtreeProd' <> rSubtreeProd) VGM.unsafeWrite sLct i size'' VGM.unsafeWrite prodLct i prod'' VGM.unsafeWrite dualProdLct i dualProd'' VGM.unsafeWrite subtreeProdLct i subtreeProd'' -- | \(O(1)\) Called on adding a path-parent edge. This is for subtree folding. {-# INLINEABLE addLightST #-} addLightST :: (Semigroup a, VU.Unbox a) => Lct s a -> Vertex -> Vertex -> ST s () addLightST Lct {subtreeProdLct, midLct} p c = do newChild <- VGM.unsafeRead subtreeProdLct c VGM.unsafeModify midLct (newChild <>) p -- | \(O(1)\) Called on changing a path-parent edge. This is for subtree folding. {-# INLINEABLE changeLightST #-} changeLightST :: Lct s a -> Vertex -> Vertex -> Vertex -> ST s () changeLightST _lct _u _v _p = do pure () -- | \(O(1)\) Called on erasing a path-parent edge. This is for subtree folding. {-# INLINEABLE eraseLightST #-} eraseLightST :: (Semigroup a, VU.Unbox a) => Lct s a -> Vertex -> Vertex -> ST s () eraseLightST Lct {subtreeProdLct, midLct, invOpLct} p c = do sub <- VGM.unsafeRead subtreeProdLct c let !sub' = invOpLct sub VGM.unsafeModify midLct (<> sub') p -- * Link/cut -- | Amortized \(O(\log n)\). {-# INLINEABLE linkST #-} linkST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> Vertex -> ST s () linkST lct@Lct {pLct, rLct} c p = do -- make @c@ the new root of the underlying tree evertST lct c -- remove right children of @p@. _ <- exposeST lct p pushNodeST lct p -- no assertion and very unsafe.. -- dbgM $ do -- cp <- VGM.unsafeRead pLct c -- let !_ = ACIA.runtimeAssert (nullLct cp) $ "cp must be null: " ++ show (c, cp) -- pr <- VGM.unsafeRead rLct p -- let !_ = ACIA.runtimeAssert (nullLct pr) $ "pr must be null: " ++ show (p, pr) -- pure () -- connect with a heavy edge: VGM.unsafeWrite pLct c p VGM.unsafeWrite rLct p c updateNodeST lct p -- no assertion and very unsafe.. {-# INLINEABLE cutST #-} cutST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> Vertex -> ST s () cutST lct@Lct {pLct, lLct} u v = do -- make @u@ the new root of the underlying tree evertST lct u -- make @v@ in the same preferred path as the root _ <- exposeST lct v -- dbgM $ do -- -- @v@ does not have any right children. because @u@ and @v@ are neighbors, @vl@ is @u@. -- vp <- VGM.unsafeRead pLct v -- let !_ = ACIA.runtimeAssert (nullLct vp) "vp must be null" -- vl <- VGM.unsafeRead lLct v -- let !_ = ACIA.runtimeAssert (vl == u) "vl must be `u`" -- pure () -- do -- -- @v@ does not have any right children. because @u@ and @v@ are neighbors, @vl@ is @u@. -- vp <- VGM.unsafeRead pLct v -- vl <- VGM.unsafeRead lLct v -- let !_ = if nullLct vp then () else error "vp must be null" -- let !_ = if vl == u then () else error "vl must be `u`" -- pure () -- delete the heavy edge. -- vl <- VGM.unsafeRead lLct v -- VGM.unsafeWrite pLct vl undefLct VGM.unsafeWrite pLct u undefLct VGM.unsafeWrite lLct v undefLct updateNodeST lct v -- | Amortized \(O(\log n)\). Makes \(v\) a new root of the underlying tree. {-# INLINEABLE evertST #-} evertST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> ST s () evertST lct v = do -- make @v@ be in the same preferred path as root. note that @v@ is at the root of the auxiliary tree. _ <- exposeST lct v -- reverse all the edges with respect to @v@: make @v@ a new root of the auxiliary tree. reverseNodeST lct v pushNodeST lct v {-# INLINEABLE exposeST #-} exposeST :: (Monoid a, VU.Unbox a) => Lct s a -> Vertex -> ST s Vertex exposeST lct@Lct {pLct, rLct} v0 = do let inner v lastRoot | nullLct v = pure lastRoot | otherwise = do -- go up to the top of the auxiliary tree: splayST lct v -- make @lastRoot@ the right child of @v@: -- v v -- /|\ -> /|\ -- | r | lastRoot <-- @v0@ (in the @lastRoot@) will be connected to the root -- lastRoot r r <- VGM.unsafeRead rLct v unless (nullLct r) $ addLightST lct v r unless (nullLct lastRoot) $ eraseLightST lct v lastRoot VGM.unsafeWrite rLct v lastRoot updateNodeST lct v -- go up to the next auxiliary tree: -- p -- | -- v -- \ -- lastRoot vp <- VGM.unsafeRead pLct v inner vp v res <- inner v0 undefLct -- do -- -- FIXME: remove -- pRes <- VGM.unsafeRead pLct res -- unless (nullLct pRes) $ error $ "must be null!!! " ++ show (res, pRes) splayST lct v0 -- do -- -- FIXME: remove -- p <- VGM.unsafeRead pLct v0 -- unless (nullLct p) $ error $ "must be null!!! " ++ show (res, v0, p) pure res -- * Jump, LCA {-# INLINEABLE rootST #-} rootST :: (HasCallStack, Monoid a, VU.Unbox a) => Lct s a -> Int -> ST s Vertex rootST lct@Lct {lLct} c0 = do _ <- exposeST lct c0 pushNodeST lct c0 let inner c = do cl <- VGM.unsafeRead lLct c if nullLct cl then pure c else do pushNodeST lct cl inner cl c' <- inner c0 splayST lct c' pure c' where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.rootST" c0 (nLct lct) {-# INLINEABLE parentST #-} parentST :: (HasCallStack, Monoid a, VU.Unbox a) => Lct s a -> Int -> ST s (Maybe Vertex) parentST lct@Lct {lLct, rLct} x = do _ <- exposeST lct x pushNodeST lct x xl <- VGM.unsafeRead lLct x if nullLct xl then pure Nothing else do pushNodeST lct xl let inner y = do yr <- VGM.unsafeRead rLct y if nullLct yr then pure y else do pushNodeST lct yr inner yr Just <$> inner xl where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.parentST" x (nLct lct) {-# INLINEABLE jumpMaybeST #-} jumpMaybeST :: (HasCallStack, Monoid a, VU.Unbox a) => Lct s a -> Vertex -> Vertex -> Int -> ST s (Maybe Vertex) jumpMaybeST lct@Lct {lLct, rLct, sLct} u0 v0 k0 = do -- make @v0@ a new root of the underlying tree evertST lct v0 -- make @u0@ in the same preferred path as the root (@v0) _ <- exposeST lct u0 size <- VGM.unsafeRead sLct u0 if 0 <= k0 && k0 < size then do let inner k u = do pushNodeST lct u -- TODO: what is happening? ur <- VGM.unsafeRead rLct u urSize <- if nullLct ur then pure 0 else VGM.unsafeRead sLct ur case compare k urSize of LT -> inner k ur EQ -> pure u GT -> do ul <- VGM.unsafeRead lLct u inner (k - (urSize + 1)) ul res <- inner k0 u0 splayST lct res pure $ Just res else do pure Nothing {-# INLINEABLE prodSubtreeST #-} prodSubtreeST :: (HasCallStack, Monoid a, VU.Unbox a) => -- | Link/cut tree Lct s a -> -- | Vertex Vertex -> -- | Parent \(p\) (not need be adjacent to \(v\)), or same as \(v\), making it a new root. Vertex -> -- | Subtree's monoid product ST s a prodSubtreeST lct@Lct {nLct, subtreeProdLct} v rootOrParent = do if v == rootOrParent then do -- `v` will be the root evertST lct v VGM.unsafeRead subtreeProdLct v else do -- retrieve the adjacent parent: parent_ <- fromJust <$> jumpMaybeST lct v rootOrParent 1 -- detach @v@ from the parent. now that it's the root of the subtree vertices, the aggregation -- value is the aggregation of all the subtree vertices. cutST lct v parent_ res <- VGM.unsafeRead subtreeProdLct v -- attach again linkST lct v parent_ pure res where !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.prodSubtreeST" v nLct !_ = ACIA.checkIndex "AtCoder.Extra.Tree.Lct.prodSubtreeST" rootOrParent nLct {-# INLINEABLE lengthBetweenST #-} lengthBetweenST :: (HasCallStack, Monoid a, VU.Unbox a) => Lct s a -> Vertex -> Vertex -> ST s Int lengthBetweenST lct@Lct {sLct} u0 v0 = do -- make @v0@ a new root of the underlying tree evertST lct v0 -- make @u0@ in the same preferred path as the root (@v0) _ <- exposeST lct u0 size <- VGM.unsafeRead sLct u0 pure $ size - 1