-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMo.hs
64 lines (52 loc) · 2.24 KB
/
Mo.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
{-|
Mo's algorithm
Useful for answering certain range queries on a static sequence. The algorithm works by maintaining
some state for a current range, and updating the state by adding or removing elements one by one.
An important parameter for the algorithm is the block size. If the range size is n, number of
queries is q, block size is b, state update takes time f(n), getting the answer for the current
state takes g(n), then the algorithm answers all queries in
O(q log q + bq * f(n) + n^2/b * f(n) + q * g(n))
A suitable value of block size minimizes run time.
For example, when q ~ n, f(n) = O(1), g(n) = O(1), setting b = sqrt(n) gives run time O(n sqrt(n)).
For f(n) = O(log n), a better choice of b is sqrt(n/log n) giving run time O(n sqrt(n log n)). In
practice, it works to experiment a bit and choose a good fixed value.
Sources:
* https://cp-algorithms.com/data_structures/sqrt_decomposition.html
runMo
Run Mo's algorithm given the block size, state update and answer functions, and queries. See above
for time complexity.
sqrtSize
Square root of an Int, rounded to an Int. O(1).
-}
module Mo
( MoQuery(..)
, Tag
, runMo
, sqrtSize
) where
import Control.DeepSeq
import Data.List
type Tag = Int
data MoQuery = MoQuery { ql_ :: !Int, qr_ :: !Int, qtag_ :: !Tag } deriving Show
runMo :: Monad m => Int -> (Int -> m ()) -> (Int -> m ()) -> m a -> [MoQuery] -> m [(Tag, a)]
runMo _ _ _ _ [] = pure []
runMo bsize add rem ans qrys = go qrys' start (start-1) [] where
cmp (MoQuery l1 r1 _) (MoQuery l2 r2 _) = compare b1 b2 <> rc where
(b1, b2) = (l1 `div` bsize, l2 `div` bsize)
rc = if even b1 then compare r1 r2 else compare r2 r1
qrys' = sortBy cmp qrys
MoQuery start _ _ = head qrys'
go [] _ _ acc = pure acc
go ((MoQuery ql qr qtag):qrys) l r acc = do
mapM_ add $ [l-1, l-2 .. ql] ++ [r+1 .. qr]
mapM_ rem $ [l .. ql-1] ++ [r, r-1 .. qr+1]
x <- ans
go qrys ql qr ((qtag, x):acc)
sqrtSize :: Int -> Int
sqrtSize n = round $ sqrt (fromIntegral n :: Double)
--------------------------------------------------------------------------------
-- For tests
-- Allows specialization across modules
{-# INLINABLE runMo #-}
instance NFData MoQuery where
rnf = rwhnf