ac-library-hs-1.5.3.0: src/AtCoder/Extra/DsuMonoid.hs
{-# LANGUAGE RecordWildCards #-}
-- | A disjoint set union with commutative monoid values associated with each group.
--
-- ==== __Example__
--
-- >>> import AtCoder.Extra.DsuMonoid qualified as Dm
-- >>> import Data.Semigroup (Sum (..))
-- >>> import Data.Vector.Unboxed qualified as VU
-- >>> dsu <- Dm.build $ VU.generate 4 Sum
-- >>> Dm.merge dsu 0 1
-- 0
--
-- >>> Dm.read dsu 0
-- Sum {getSum = 1}
--
-- >>> Dm.read dsu 1
-- Sum {getSum = 1}
--
-- >>> Dm.mergeMaybe dsu 0 2
-- Just 0
--
-- >>> Dm.read dsu 0
-- Sum {getSum = 3}
--
-- @since 1.5.3.0
module AtCoder.Extra.DsuMonoid
( -- * Disjoint set union
DsuMonoid (dsuDm, mDm),
-- * Constructors
new,
build,
-- * Merging
merge,
mergeMaybe,
merge_,
-- * Leader
leader,
-- * Component information
same,
size,
groups,
-- * Monoid values
read,
unsafeRead,
unsafeWrite,
)
where
import AtCoder.Dsu qualified as Dsu
import Control.Monad.Primitive (PrimMonad, PrimState, stToPrim)
import Data.Vector qualified as V
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)
-- | A disjoint set union with commutative monoid values associated with each group.
--
-- @since 1.5.3.0
data DsuMonoid s a = DsuMonoid
{ -- | The original DSU.
--
-- @since 1.5.3.0
dsuDm :: {-# UNPACK #-} !(Dsu.Dsu s),
-- | Commutative monoid values for each group.
--
-- @since 1.5.3.0
mDm :: !(VUM.MVector s a)
}
-- | Creates an undirected graph with \(n\) vertices and \(0\) edges.
--
-- ==== Constraints
-- - \(0 \le n\)
--
-- ==== Complexity
-- - \(O(n)\)
--
-- @since 1.5.3.0
{-# INLINE new #-}
new :: (PrimMonad m, Monoid a, VU.Unbox a) => Int -> m (DsuMonoid (PrimState m) a)
new n
| n >= 0 = build $ VU.replicate n mempty
| otherwise = error $ "AtCoder.Extra.DsuMonoid: given negative size (`" ++ show n ++ "`)"
-- | Creates an undirected graph with \(n\) vertices and \(0\) edges.
--
-- ==== Constraints
-- - \(0 \le n\)
--
-- ==== Complexity
-- - \(O(n)\)
--
-- @since 1.5.3.0
{-# INLINE build #-}
build :: (PrimMonad m, Semigroup a, VU.Unbox a) => VU.Vector a -> m (DsuMonoid (PrimState m) a)
build ms = stToPrim $ do
dsuDm <- Dsu.new $ VU.length ms
mDm <- VU.thaw ms
pure $ DsuMonoid {..}
-- | Adds an edge \((a, b)\). If the vertices \(a\) and \(b\) are in the same connected component, it
-- returns the representative (`leader`) of this connected component. Otherwise, it returns the
-- representative of the new connected component.
--
-- ==== Constraints
-- - \(0 \leq a < n\)
-- - \(0 \leq b < n\)
--
-- ==== Complexity
-- - \(O(\alpha(n))\) amortized
--
-- @since 1.5.3.0
{-# INLINEABLE merge #-}
merge :: (HasCallStack, PrimMonad m, Semigroup a, VU.Unbox a) => DsuMonoid (PrimState m) a -> Int -> Int -> m Int
merge DsuMonoid {..} a b = stToPrim $ do
r1 <- Dsu.leader dsuDm a
r2 <- Dsu.leader dsuDm b
if r1 == r2
then pure r1
else do
!m1 <- VGM.read mDm r1
!m2 <- VGM.read mDm r2
r' <- Dsu.merge dsuDm a b
VGM.write mDm r' $! m1 <> m2
pure r'
-- | Adds an edge \((a, b)\). It returns the representative of the new connected component, or
-- `Nothing` if the two vertices are in the same connected component.
--
-- ==== Constraints
-- - \(0 \leq a < n\)
-- - \(0 \leq b < n\)
--
-- ==== Complexity
-- - \(O(\alpha(n))\) amortized
--
-- @since 1.2.4.0
{-# INLINEABLE mergeMaybe #-}
mergeMaybe :: (HasCallStack, PrimMonad m, Semigroup a, VU.Unbox a) => DsuMonoid (PrimState m) a -> Int -> Int -> m (Maybe Int)
mergeMaybe DsuMonoid {..} a b = stToPrim $ do
r1 <- Dsu.leader dsuDm a
r2 <- Dsu.leader dsuDm b
if r1 == r2
then pure Nothing
else do
!m1 <- VGM.read mDm r1
!m2 <- VGM.read mDm r2
r' <- Dsu.merge dsuDm a b
VGM.write mDm r' $! m1 <> m2
pure $ Just r'
-- | `merge` with the return value discarded.
--
-- ==== Constraints
-- - \(0 \leq a < n\)
-- - \(0 \leq b < n\)
--
-- ==== Complexity
-- - \(O(\alpha(n))\) amortized
--
-- @since 1.5.3.0
{-# INLINE merge_ #-}
merge_ :: (PrimMonad m, Semigroup a, VU.Unbox a) => DsuMonoid (PrimState m) a -> Int -> Int -> m ()
merge_ dsu a b = do
_ <- merge dsu a b
pure ()
-- | Returns whether the vertices \(a\) and \(b\) are in the same connected component.
--
-- ==== Constraints
-- - \(0 \leq a < n\)
-- - \(0 \leq b < n\)
--
-- ==== Complexity
-- - \(O(\alpha(n))\) amortized
--
-- @since 1.5.3.0
{-# INLINE same #-}
same :: (HasCallStack, PrimMonad m) => DsuMonoid (PrimState m) a -> Int -> Int -> m Bool
same dsu = Dsu.same (dsuDm dsu)
-- | Returns the representative of the connected component that contains the vertex \(a\).
--
-- ==== Constraints
-- - \(0 \leq a \lt n\)
--
-- ==== Complexity
-- - \(O(\alpha(n))\) amortized
--
-- @since 1.5.3.0
{-# INLINE leader #-}
leader :: (HasCallStack, PrimMonad m) => DsuMonoid (PrimState m) a -> Int -> m Int
leader dsu = Dsu.leader (dsuDm dsu)
-- | Returns the size of the connected component that contains the vertex \(a\).
--
-- ==== Constraints
-- - \(0 \leq a < n\)
--
-- ==== Complexity
-- - \(O(\alpha(n))\)
--
-- @since 1.5.3.0
{-# INLINE size #-}
size :: (HasCallStack, PrimMonad m) => DsuMonoid (PrimState m) a -> Int -> m Int
size dsu = Dsu.size (dsuDm dsu)
-- | \O(n)\) Divides the graph into connected components and returns the vector of them.
--
-- More precisely, it returns a vector of the "vector of the vertices in a connected component".
-- Both of the orders of the connected components and the vertices are undefined.
--
-- @since 1.5.3.0
{-# INLINE groups #-}
groups :: (PrimMonad m) => DsuMonoid (PrimState m) a -> m (V.Vector (VU.Vector Int))
groups dsu = Dsu.groups (dsuDm dsu)
-- | \(O(1)\) Reads the group value of the \(k\)-th node.
--
-- @since 1.5.3.0
{-# INLINE read #-}
read :: (PrimMonad m, VU.Unbox a) => DsuMonoid (PrimState m) a -> Int -> m a
read DsuMonoid {..} i = do
VGM.read mDm =<< Dsu.leader dsuDm i
-- | \(O(1)\) Reads the group value of the \(k\)-th node.
--
-- @since 1.5.3.0
{-# INLINE unsafeRead #-}
unsafeRead :: (PrimMonad m, VU.Unbox a) => DsuMonoid (PrimState m) a -> Int -> m a
unsafeRead DsuMonoid {..} i = do
VGM.read mDm i
-- | \(O(1)\) Writes to the group value of the \(k\)-th node.
--
-- @since 1.5.3.0
{-# INLINE unsafeWrite #-}
unsafeWrite :: (PrimMonad m, VU.Unbox a) => DsuMonoid (PrimState m) a -> Int -> a -> m ()
unsafeWrite DsuMonoid {..} i x = do
VGM.write mDm i x