Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
Lazily propageted segment tree, where we can perform operation over intervals.
Algebra
\((Op * Acc) \diamond (Op * Acc) = (Op \diamond Op) * (Acc \diamond Acc)\)
Typical example of Op
is a matrix and Acc
is a column vector.
Typical problems
Synopsis
- data LazySegmentTree a op s = LazySegmentTree !(MVector s a) !(MVector s op) !Int
- newLSTreeImpl :: (Monoid a, Unbox a, Monoid op, Unbox op, PrimMonad m) => Int -> m (LazySegmentTree a op (PrimState m))
- newLSTree :: (Unbox a, Monoid a, Monoid op, Unbox op, PrimMonad m) => Int -> m (LazySegmentTree a op (PrimState m))
- generateLSTreeImpl :: (HasCallStack, Monoid a, Unbox a, Monoid op, Unbox op, PrimMonad m) => Int -> (Int -> a) -> m (LazySegmentTree a op (PrimState m))
- generateLSTree :: (HasCallStack, Unbox a, Monoid a, Monoid op, Unbox op, PrimMonad m) => Int -> (Int -> a) -> m (LazySegmentTree a op (PrimState m))
- buildLSTree :: (HasCallStack, Unbox a, Monoid a, Monoid op, Unbox op, PrimMonad m) => Vector a -> m (LazySegmentTree a op (PrimState m))
- foldLSTree :: forall a op m. (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> m a
- foldMayLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> m (Maybe a)
- readLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> m a
- foldAllLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> m a
- sactLSTree :: forall a op m. (Semigroup a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> op -> m ()
- sactAtLSTree :: (Semigroup a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> op -> m ()
- _propDownFromRoot :: (HasCallStack, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> m ()
- _sactAt :: (HasCallStack, Unbox a, Semigroup op, SegmentAction op a, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> op -> m ()
- _propAt :: (HasCallStack, Unbox a, Monoid op, Eq op, SegmentAction op a, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> m ()
- bisectLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> (a -> Bool) -> m (Maybe Int, Maybe Int)
- bisectLSTreeL :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> (a -> Bool) -> m (Maybe Int)
- bisectLSTreeR :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> (a -> Bool) -> m (Maybe Int)
Documentation
data LazySegmentTree a op s #
Lazy segment tree.
(Internal) 1-based indices
Use 1-based indices for handy vertex indices:
[ 1 ] | [ 2 ] [ 3 ] | height = 4 = log_2 16 [ 4 ] [ 5 ] [ 6 ] [ 7 ] | [08] [09] [10] [11] [12] [13] [14] [15] v ^ +-- nVerts / 2 0 1 2 3 4 5 6 7 -- iLeaf - parent = v .>>. 1 - childL = v .<<. 1 - childR = v .<<. 1 .|. 1
Invariant
- New operators always come from the left: \((\mathcal{newOp} <> \mathcal{oldOp}) \mathcal{acc}\) or \(\mathcal{newOp} `\mathcal{sact}` (\mathcal{oldOp} `\mathcal{sact}` \mathcal{acc})\).
- Folding is performed from the left to the right. Use
Dual
monoid if you need to reverse the folding direction.
Internals
See also: https://maspypy.com/segment-tree-%E3%81%AE%E3%81%8A%E5%8B%89%E5%BC%B72
Bottom-up folding
Bottom-up folding often needs less traversal than a top-down folding.
[-----------------------------] [-]: vertices [-------------] [-------------] [*]: folded vertices ("glitch segments") [-----] [*****] [*****] [-----] [-] [*] [-] [-] [-] [-] [-] [-] ---------------- folding interval
Lazily propagated values for children
Each vertex has lazily propagated values. These values are stored for children and the value for the vertex is applied instantly. This lets the parent evaluation be done without checking the child's propagated value.
Propagation pruning
Propagations are only performed over the parent vertices of folded vertives:
[+++++++++++++++++++++++++++++] [-]: vertices [+++++++++++++] [+++++++++++++] [*]: folded vertices ("glitch segments") [-----] [*****] [*****] [-----] [+]: propagated vertices (above the "glitch segments") [-] [*] [-] [-] [-] [-] [-] [-] ---------------- folding interval
Note that the propagted vertices are pruned to be above the glitch segments only.
LazySegmentTree !(MVector s a) !(MVector s op) !Int |
newLSTreeImpl :: (Monoid a, Unbox a, Monoid op, Unbox op, PrimMonad m) => Int -> m (LazySegmentTree a op (PrimState m)) #
\(O(N)\) Creates a LazySegmentTree
with mempty
as the initial accumulated values.
newLSTree :: (Unbox a, Monoid a, Monoid op, Unbox op, PrimMonad m) => Int -> m (LazySegmentTree a op (PrimState m)) #
\(O(N)\)
generateLSTreeImpl :: (HasCallStack, Monoid a, Unbox a, Monoid op, Unbox op, PrimMonad m) => Int -> (Int -> a) -> m (LazySegmentTree a op (PrimState m)) #
\(O(N)\) Creates LazySegmentTree
with initial leaf values.
generateLSTree :: (HasCallStack, Unbox a, Monoid a, Monoid op, Unbox op, PrimMonad m) => Int -> (Int -> a) -> m (LazySegmentTree a op (PrimState m)) #
\(O(N)\)
buildLSTree :: (HasCallStack, Unbox a, Monoid a, Monoid op, Unbox op, PrimMonad m) => Vector a -> m (LazySegmentTree a op (PrimState m)) #
\(O(N)\). TODO: Share the internal implementation with genearteLSTree
takeing filling function.
Action with length given by the segment tree
foldLSTree :: forall a op m. (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> m a #
\(O(\log N)\)
foldMayLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> m (Maybe a) #
\(O(\log N)\)
readLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> m a #
\(O(\log N)\) Read one leaf. TODO: Faster implementation.
foldAllLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> m a #
\(O(\log N)\)
sactLSTree :: forall a op m. (Semigroup a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> op -> m () #
\(O(\log N)\) Applies a lazy operator monoid over an interval, propagated lazily.
sactAtLSTree :: (Semigroup a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> op -> m () #
\(O(\log N)\) Acts on one leaf. TODO: Specialize the implementation.
_propDownFromRoot :: (HasCallStack, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> m () #
\(O(\log N)\) Propagates the lazy operator monoids from the root to just before the glitch segments.
iLeaf
: Given with zero-based index.
Pruning
The propagation is performed from the root to just before the folded vertices. In other words, propagation is performed just before performing the first glitch. That's enough for both folding and acting.
_sactAt :: (HasCallStack, Unbox a, Semigroup op, SegmentAction op a, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> op -> m () #
\(O(1)\) Acts on a node.
Evaluation strategy
- The propagated value for the children are stored and propagated lazily.
- The propagated value to the vertex is evaluated instantly.
Invariants
- The new coming operator operator always comes from the left.
_propAt :: (HasCallStack, Unbox a, Monoid op, Eq op, SegmentAction op a, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> m () #
Propagates the operator onto the children. Push.
Bisection methods
bisectLSTree :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> (a -> Bool) -> m (Maybe Int, Maybe Int) #
\(O(\log^2 N)\) The l
, r
indices are the zero-based leaf indices.
bisectLSTreeL :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> (a -> Bool) -> m (Maybe Int) #
\(O(\log^2 N)\)
bisectLSTreeR :: (HasCallStack, Monoid a, Unbox a, Monoid op, SegmentAction op a, Eq op, Unbox op, PrimMonad m) => LazySegmentTree a op (PrimState m) -> Int -> Int -> (a -> Bool) -> m (Maybe Int) #
\(O(\log^2 N)\)