-
Notifications
You must be signed in to change notification settings - Fork 1
/
RMQ.ml
171 lines (140 loc) · 4.56 KB
/
RMQ.ml
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
(* Various implementations of Range Minimum Query
See https://en.wikipedia.org/wiki/Range_minimum_query
*)
module type OrderedType = sig
type t
val compare: t -> t -> int
end
module type RMQ = sig
type elt
type t
val create: elt array -> t
val rmq: t -> lo:int -> hi:int -> elt
end
let check n lo hi =
if not (0 <= lo && lo < hi && hi <= n) then
invalid_arg "rmq"
(* Naive minimum in a possibly empty segment
PRE 0 <= lo <= hi <= n *)
let rec fold cmp a m ~lo ~hi =
if lo = hi then m else
let x = Array.unsafe_get a lo in
let m = if cmp x m < 0 then x else m in
fold cmp a m ~lo:(lo + 1) ~hi
(* Naive minimum in a non-empty segment
PRE 0 <= lo < hi <= n *)
let naive cmp a ~lo ~hi =
fold cmp a (Array.unsafe_get a lo) ~lo:(lo+1) ~hi
let min cmp x y =
if cmp x y < 0 then x else y
module Make0(E: OrderedType) : RMQ with type elt = E.t = struct
type elt = E.t
type t = elt array
let create a =
a
let rmq a ~lo ~hi =
check (Array.length a) lo hi;
naive E.compare a ~lo ~hi
end
(* Pre-processing in O(sqrt n) and request in O(sqrt n)
Idea: pre-compute the minimum of blocks of size sqrt(n) :
+-------+-------+-------+-------+
blk | | | | |
+-------+-------+-------+-------+
arr | | | | | | | | | | | | | | | | |
+-------+-------+-------+-------+
Then the answer is the minimum of
- possibly full blocks between lo and hi
- possibly the end of the block containing lo
- possibly the start of the block containing hi
Example:
fb lb
+-------+-------+-------+-------+-------+
blk | | x | x | | |
+-------+-------+-------+-------+-------+
arr | | |x|x| | | | | | | | |x| | | | | | | |
+-------+-------+-------+-------+-------+
^ ^
lo hi
*)
module Make1(E: OrderedType) : RMQ with type elt = E.t = struct
type elt = E.t
type t = {
arr: elt array;
bsz: int; (* block size *)
blk: elt array; (* block minima *)
eob: int; (* end of blocks in [a] *)
}
let create a =
let n = Array.length a in
let bsz = if n < 9 then n else truncate (float n ** 0.5) in
assert (bsz > 0);
let block i =
let lo = i * bsz in
naive E.compare a ~lo ~hi:(lo + bsz) in
let m = n / bsz in
let blk = Array.init m block in
let eob = m * bsz in
assert (eob <= n);
{ arr = a; bsz; blk; eob }
let rmq {arr; bsz; blk; eob} ~lo ~hi =
check (Array.length arr) lo hi;
let fb = (lo + bsz - 1) / bsz in
let lb = hi / bsz in
if fb >= lb then naive E.compare arr ~lo ~hi else
let m = naive E.compare blk ~lo:fb ~hi:lb in
let m = fold E.compare arr m ~lo ~hi:(fb * bsz) in
fold E.compare arr m ~lo:(lb * bsz) ~hi
end
(* Pre-processing in O(n log n) and request in O(1)
Idea: pre-compute all
t[k][i] = min arr[i + j]
0<=j<2^k
for 0 <= i < n and 0 <= k <= log2(n).
Example:
+-------+-------+-------+-------+
arr | | | | | | | | | | | | | | | | |
+-------+-------+-------+-------+
<-------------->
t[2][3]
Then the answer is min t[lo][k] t[hi-2^k][k]
for the smallest k st 2^k >= (hi-lo)/2.
*)
module Make2(E: OrderedType) : RMQ with type elt = E.t = struct
type elt = E.t
type t = {
t: elt array array;
}
let rec log2 x = if x = 0 then -1 else 1 + log2 (x lsr 1)
let log2_t8 = Array.init 256 log2
let log2_16 x =
if x land 0xFF00 = 0 then Array.unsafe_get log2_t8 x
else 8 + Array.unsafe_get log2_t8 (x lsr 8)
let log2_32 x =
if x land 0xFFFF_0000 = 0 then log2_16 x else 16 + log2_16 (x lsr 16)
let log2 x =
if x land 0x7FFF_FFFF_0000_0000 = 0 then log2 x else 32 + log2 (x lsr 32)
let create a =
let n = Array.length a in
let m = 1 + log2 n in
let t = Array.make m a in
for k = 1 to m - 1 do
let w = 1 lsl (k - 1) in
let prev = t.(k - 1) in
t.(k) <- Array.init (n - 2*w + 1)
(fun i -> min E.compare prev.(i) prev.(i + w))
done;
{ t }
let rmq {t} ~lo ~hi =
check (Array.length t.(0)) lo hi;
let w = hi - lo in
(* a single element *)
if w = 0 then Array.unsafe_get (Array.unsafe_get t 0) lo else
let k = log2 w in
(* exactly a power of 2 => a single lookup *)
if w land (-w) = w then Array.unsafe_get (Array.unsafe_get t k) lo else
(* otherwise, two overlapping lookups *)
let tk = Array.unsafe_get t k in
min E.compare (Array.unsafe_get tk lo)
(Array.unsafe_get tk (hi - 1 lsl k))
end