セグメント木
参考: 競技プログラミングの鉄則
セグメント木は畳み込みのキャッシュです。
実装
『鉄則本』では完全 2 分木をバックにした実装が紹介されていました。概要だけ読んで実装しました。
ヘルパー部分
-- {{{ Bits
-- | Log base of two or bit floor.
-- | <https://hackage.haskell.org/package/base-4.17.0.0/docs/Data-Bits.html#v:countLeadingZeros>
log2 :: (FiniteBits b) => b -> Int
log2 x = finiteBitSize x - 1 - countLeadingZeros x
-- | Ceiling of log base 2 of an `Int`.
-- |
-- | # Example
-- |
-- | ```hs
-- | > log2 3
-- | 1
-- | > log2CeilInt 3
-- | 2
-- | ```
log2CeilInt :: Int -> Int
log2CeilInt x = msb + ceiling
where
msb = log2 x
ceiling = if (clearBit x msb) > 0 then 1 else 0
-- | Calculates the smallest integral power of two that is not smaller than `x`.
-- |
-- | # Example
-- |
-- | ```hs
-- | > bitCeil 3
-- | 4
-- | ```
bitCeil :: Int -> Int
bitCeil = bit . log2CeilInt
-- }}}
本実装
-- {{{ Segment tree
-- | A mutable segment tree backed by a complete binary tree.
-- |
-- | # Overview
-- |
-- | A segment tree is a cache of a folding function.
-- | Each node corresponds to a folding range and the node contains the folding result.
-- |
-- | A segment tree has a constant size and never be resized.
-- |
-- | # Operations
-- |
-- | Modification takes $O(log N)$, so creation takes $N(log N)$.
-- | Lookup takes $O(log N)$.
-- |
-- | # (Internal) Indices
-- |
-- | The complete binary tree has `2 ^ depth - 1` elements.
-- |
-- | - Child elements of a parent node `i` has index `2 * i + 1` and `2 * i + 2`.
-- | - The leaf indices start with `length / 2 - 1`.
-- |
-- | Example:
-- |
-- | ```
-- | 0
-- | 1 2
-- | 3 4 5 6
-- | 07 08 09 10 11 12 13 14
-- | ```
data MSegmentTree s a = MSegmentTree (a -> a -> a) (VUM.MVector s a)
-- TODO: Generic queries and immutable segment tree (with `Show` instance)
-- | Creates a new segment tree for `n` leaves.
{-# INLINE newTree #-}
newTree :: (VUM.Unbox a, PrimMonad m) => (a -> a -> a) -> Int -> a -> m (MSegmentTree (PrimState m) a)
newTree !f !n !value = MSegmentTree f <$> VUM.replicate n' value
where
!n' = shiftL (bitCeil n) 1
-- | Updates an `MSegmentTree` leaf value and their parents up to top root.
{-# INLINE updateLeaf #-}
updateLeaf :: (VU.Unbox a, PrimMonad m) => MSegmentTree (PrimState m) a -> Int -> a -> m ()
updateLeaf tree@(MSegmentTree _ vec) !i !value = _updateElement tree i' value
where
-- length == 2 * (the number of the leaves)
!offset = (VUM.length vec) `div` 2 - 1
!i' = i + offset
-- | (Internal) Updates an `MSegmentTree` element (node or leaf) value and their parents up to top root.
{-# INLINE _updateElement #-}
_updateElement :: (VU.Unbox a, PrimMonad m) => MSegmentTree (PrimState m) a -> Int -> a -> m ()
_updateElement tree@(MSegmentTree _ vec) !i !value = do
VUM.write vec i value
_updateParent tree ((i - 1) `div` 2)
-- | (Internal) Recursivelly updates the parent nodes.
{-# INLINE _updateParent #-}
_updateParent :: (VU.Unbox a, PrimMonad m) => MSegmentTree (PrimState m) a -> Int -> m ()
_updateParent _ (-1) = pure () -- REMARK: (-1) `div` 2 == -1
_updateParent _ 0 = pure ()
_updateParent tree@(MSegmentTree f vec) !iParent = do
!c1 <- VUM.read vec (iParent * 2 + 1)
!c2 <- VUM.read vec (iParent * 2 + 2)
_updateElement tree iParent (f c1 c2)
-- | Retrieves the folding result over the inclusive range `[l, r]` from `MSegmentTree`.
{-# INLINE queryByRange #-}
queryByRange :: forall a m. (VU.Unbox a, PrimMonad m) => MSegmentTree (PrimState m) a -> (Int, Int) -> m a
queryByRange (MSegmentTree !f !vec) (!lo, !hi) = fromJust <$> loop 0 (0, initialHi)
where
!initialHi = (VUM.length vec) `div` 2 - 1
loop :: Int -> (Int, Int) -> m (Maybe a)
loop !i (!l, !h)
| lo <= l && h <= hi = Just <$> VUM.read vec i
| h < lo || hi < l = pure Nothing
| otherwise = do
let d = (h - l) `div` 2
!ansL <- loop (2 * i + 1) (l, l + d)
!ansH <- loop (2 * i + 2) (l + d + 1, h)
pure . Just $ case (ansL, ansH) of
(Just !a, Just !b) -> f a b
(Just !a, _) -> a
(_, Just !b) -> b
(_, _) -> error "query error (segment tree)"
-- }}}