ac-library-hs-1.1.0.0: src/AtCoder/Extra/Tree.hs
-- | Generic tree functions.
--
-- @since 1.1.0.0
module AtCoder.Extra.Tree
( -- * Tree folding
-- | These function are built around the three type parameters: \(w\), \(f\) and \(a\).
--
-- - \(w\): Edge weight.
-- - \(f\): Monoid action to a vertex value. These actions are created from vertex value \(a\)
-- and edge information @(Int, w)@.
-- - \(a\): Monoid values stored at vertices.
fold,
scan,
foldReroot,
)
where
import Data.Functor.Identity (runIdentity)
import Data.Vector.Generic qualified as VG
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)
{-# INLINE foldImpl #-}
foldImpl ::
forall m w f a.
(HasCallStack, Monad m, VU.Unbox w) =>
(Int -> VU.Vector (Int, w)) ->
(Int -> a) ->
(a -> (Int, w) -> f) ->
(f -> a -> a) ->
Int ->
(Int -> a -> m ()) ->
m a
foldImpl tree valAt toF act root memo = inner (-1) root
where
inner :: Int -> Int -> m a
inner !parent !v1 = do
let !acc0 = valAt v1
let !v2s = VU.filter ((/= parent) . fst) $ tree v1
!res <- VU.foldM' (\acc (!v2, !w) -> (`act` acc) . (`toF` (v1, w)) <$> inner v1 v2) acc0 v2s
memo v1 res
pure res
-- | \(O(n)\) Folds a tree from a root vertex, also known as tree DP.
--
-- ==== __Example__
-- >>> import AtCoder.Extra.Graph qualified as Gr
-- >>> import AtCoder.Extra.Tree qualified as Tree
-- >>> import Data.Semigroup (Sum (..))
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let gr = Gr.build @(Sum Int) 5 . Gr.swapDupe $ VU.fromList [(2, 1, Sum 1), (1, 0, Sum 1), (2, 3, Sum 1), (3, 4, Sum 1)]
-- >>> type W = Sum Int -- edge weight
-- >>> type F = Sum Int -- action type
-- >>> type X = Sum Int -- vertex value
-- >>> :{
-- let res = Tree.fold (gr `Gr.adjW`) valAt toF act 2
-- where
-- valAt :: Int -> X
-- valAt = const $ mempty @(Sum Int)
-- toF :: X -> (Int, W) -> F
-- toF x (!_i, !dx) = x + dx
-- act :: F -> X -> X
-- act dx x = dx + x
-- in getSum res
-- :}
-- 4
--
-- @since 1.1.0.0
{-# INLINE fold #-}
fold ::
(HasCallStack, VU.Unbox w) =>
-- | Graph as a function.
(Int -> VU.Vector (Int, w)) ->
-- | @valAt@: Assignment of initial vertex values.
(Int -> a) ->
-- | @toF@: Converts a vertex value into an action onto a neighbor vertex.
(a -> (Int, w) -> f) ->
-- | @act@: Performs an action onto a vertex value.
(f -> a -> a) ->
-- | Root vertex.
Int ->
-- | Tree folding result from the root vertex.
a
fold tree valAt toF act root = runIdentity $ do
foldImpl tree valAt toF act root (\_ _ -> pure ())
-- | \(O(n)\) Folds a tree from a root vertex, also known as tree DP. The calculation process on
-- every vertex is recoreded and returned as a vector.
--
-- ==== __Example__
-- >>> import AtCoder.Extra.Graph qualified as Gr
-- >>> import AtCoder.Extra.Tree qualified as Tree
-- >>> import Data.Semigroup (Sum (..))
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let n = 5
-- >>> let gr = Gr.build @(Sum Int) n . Gr.swapDupe $ VU.fromList [(2, 1, Sum 1), (1, 0, Sum 1), (2, 3, Sum 1), (3, 4, Sum 1)]
-- >>> type W = Sum Int -- edge weight
-- >>> type F = Sum Int -- action type
-- >>> type X = Sum Int -- vertex value
-- >>> :{
-- let res = Tree.scan n (gr `Gr.adjW`) valAt toF act 2
-- where
-- valAt :: Int -> X
-- valAt = const $ mempty @(Sum Int)
-- toF :: X -> (Int, W) -> F
-- toF x (!_i, !dx) = x + dx
-- act :: F -> X -> X
-- act dx x = dx + x
-- in VU.map getSum res
-- :}
-- [0,1,4,1,0]
--
-- @since 1.1.0.0
{-# INLINE scan #-}
scan ::
(VU.Unbox w, VG.Vector v a) =>
-- | The number of vertices.
Int ->
-- | Graph as a function.
(Int -> VU.Vector (Int, w)) ->
-- | @valAt@: Assignment of initial vertex values.
(Int -> a) ->
-- | @toF@: Converts a vertex value into an action onto a neighbor vertex.
(a -> (Int, w) -> f) ->
-- | @act@: Performs an action onto a vertex value.
(f -> a -> a) ->
-- | Root vertex.
Int ->
-- | Tree scanning result from a root vertex.
v a
scan n tree acc0At toF act root = VG.create $ do
dp <- VGM.unsafeNew n
!_ <- foldImpl tree acc0At toF act root $ \v a -> do
VGM.unsafeWrite dp v a
pure dp
-- | \(O(n)\) Folds a tree from every vertex, using the rerooting technique.
--
-- ==== __Example__
-- >>> import AtCoder.Extra.Graph qualified as Gr
-- >>> import AtCoder.Extra.Tree qualified as Tree
-- >>> import Data.Semigroup (Sum (..))
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> let n = 5
-- >>> let gr = Gr.build @(Sum Int) n . Gr.swapDupe $ VU.fromList [(2, 1, Sum 1), (1, 0, Sum 1), (2, 3, Sum 1), (3, 4, Sum 1)]
-- >>> type W = Sum Int -- edge weight
-- >>> type F = Sum Int -- action type
-- >>> type X = Sum Int -- vertex value
-- >>> :{
-- let res = Tree.foldReroot n (gr `Gr.adjW`) valAt toF act
-- where
-- valAt :: Int -> X
-- valAt = const $ mempty @(Sum Int)
-- toF :: X -> (Int, W) -> F
-- toF x (!_i, !dx) = x + dx
-- act :: F -> X -> X
-- act dx x = dx + x
-- in VU.map getSum res
-- :}
-- [4,4,4,4,4]
--
-- @since 1.1.0.0
{-# INLINE foldReroot #-}
foldReroot ::
forall w f a.
(HasCallStack, VU.Unbox w, VU.Unbox a, VU.Unbox f, Monoid f) =>
-- | The number of vertices.
Int ->
-- | Graph as a function.
(Int -> VU.Vector (Int, w)) ->
-- | @valAt@:Assignment of initial vertex values.
(Int -> a) ->
-- | @toF@: Converts a vertex value into an action onto a neighbor vertex.
(a -> (Int, w) -> f) ->
-- | @act@: Performs an action onto a vertex value.
(f -> a -> a) ->
-- | Tree folding result from every vertex as a root.
VU.Vector a
foldReroot n tree valAt toF act = VU.create $ do
-- Calculate tree DP for every vertex as a root:
!dp <- VUM.unsafeNew n
let reroot parent parentF v1 = do
-- TODO: when the operator is not commutative?
let !children = VU.filter ((/= parent) . fst) $ tree v1
let !fL = VU.scanl' (\ !f (!v2, !w) -> (f <>) . (`toF` (v1, w)) $ treeDp VG.! v2) f0 children
let !fR = VU.scanr' (\(!v2, !w) !f -> (<> f) . (`toF` (v1, w)) $ treeDp VG.! v2) f0 children
-- save
let !x1 = (parentF <> VU.last fL) `act` valAt v1
VGM.unsafeWrite dp v1 x1
VU.iforM_ children $ \i2 (!v2, !w) -> do
-- composited operator excluding @v2@:
let !f1 = parentF <> (fL VG.! i2) <> (fR VG.! (i2 + 1))
let !v1Acc = f1 `act` valAt v1
let !f2 = toF v1Acc (v2, w)
reroot v1 f2 v2
reroot (-1 :: Int) f0 root0
pure dp
where
!root0 = 0 :: Int
!f0 = mempty @f
!treeDp = scan n tree valAt toF act root0 :: VU.Vector a