ac-library-rb 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/unittest.yml +16 -0
- data/.gitignore +11 -0
- data/.rubocop.yml +198 -0
- data/Gemfile +3 -0
- data/LICENSE +116 -0
- data/README.ja.md +56 -0
- data/README.md +41 -0
- data/Rakefile +11 -0
- data/ac-library-rb.gemspec +32 -0
- data/bin/console +8 -0
- data/bin/lock_lib.rb +27 -0
- data/bin/setup +8 -0
- data/document_en/binary_index_tree.md +3 -0
- data/document_en/convolution.md +67 -0
- data/document_en/dsu.md +132 -0
- data/document_en/fenwick_tree.md +99 -0
- data/document_en/index.md +79 -0
- data/document_en/lazy_segtree.md +141 -0
- data/document_en/math.md +104 -0
- data/document_en/max_flow.md +165 -0
- data/document_en/min_cost_flow.md +132 -0
- data/document_en/modint.md +263 -0
- data/document_en/priority_queue.md +119 -0
- data/document_en/segtree.md +134 -0
- data/document_en/string.md +106 -0
- data/document_en/two_sat.md +91 -0
- data/document_en/union_find.md +3 -0
- data/document_ja/convolution.md +64 -0
- data/document_ja/dsu.md +183 -0
- data/document_ja/fenwick_tree.md +83 -0
- data/document_ja/index.md +89 -0
- data/document_ja/lazy_segtree.md +135 -0
- data/document_ja/math.md +116 -0
- data/document_ja/max_flow.md +129 -0
- data/document_ja/min_cost_flow.md +105 -0
- data/document_ja/modint.md +349 -0
- data/document_ja/priority_queue.md +103 -0
- data/document_ja/scc.md +65 -0
- data/document_ja/segtree.md +145 -0
- data/document_ja/string.md +105 -0
- data/document_ja/two_sat.md +87 -0
- data/lib/ac-library-rb/version.rb +3 -0
- data/lib/convolution.rb +124 -0
- data/lib/core_ext/modint.rb +19 -0
- data/lib/crt.rb +52 -0
- data/lib/dsu.rb +44 -0
- data/lib/fenwick_tree.rb +48 -0
- data/lib/floor_sum.rb +21 -0
- data/lib/inv_mod.rb +26 -0
- data/lib/lazy_segtree.rb +149 -0
- data/lib/lcp_array.rb +23 -0
- data/lib/max_flow.rb +137 -0
- data/lib/min_cost_flow.rb +143 -0
- data/lib/modint.rb +170 -0
- data/lib/pow_mod.rb +13 -0
- data/lib/priority_queue.rb +89 -0
- data/lib/scc.rb +77 -0
- data/lib/segtree.rb +140 -0
- data/lib/suffix_array.rb +128 -0
- data/lib/two_sat.rb +34 -0
- data/lib/z_algorithm.rb +32 -0
- data/lib_helpers/ac-library-rb/all.rb +22 -0
- data/lib_lock/ac-library-rb.rb +22 -0
- data/lib_lock/ac-library-rb/convolution.rb +126 -0
- data/lib_lock/ac-library-rb/core_ext/modint.rb +19 -0
- data/lib_lock/ac-library-rb/crt.rb +54 -0
- data/lib_lock/ac-library-rb/dsu.rb +46 -0
- data/lib_lock/ac-library-rb/fenwick_tree.rb +50 -0
- data/lib_lock/ac-library-rb/floor_sum.rb +23 -0
- data/lib_lock/ac-library-rb/inv_mod.rb +28 -0
- data/lib_lock/ac-library-rb/lazy_segtree.rb +151 -0
- data/lib_lock/ac-library-rb/lcp_array.rb +25 -0
- data/lib_lock/ac-library-rb/max_flow.rb +139 -0
- data/lib_lock/ac-library-rb/min_cost_flow.rb +145 -0
- data/lib_lock/ac-library-rb/modint.rb +172 -0
- data/lib_lock/ac-library-rb/pow_mod.rb +15 -0
- data/lib_lock/ac-library-rb/priority_queue.rb +91 -0
- data/lib_lock/ac-library-rb/scc.rb +79 -0
- data/lib_lock/ac-library-rb/segtree.rb +142 -0
- data/lib_lock/ac-library-rb/suffix_array.rb +130 -0
- data/lib_lock/ac-library-rb/two_sat.rb +36 -0
- data/lib_lock/ac-library-rb/z_algorithm.rb +34 -0
- metadata +158 -0
data/lib/pow_mod.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Priority Queue
|
2
|
+
# Reference: https://github.com/python/cpython/blob/master/Lib/heapq.py
|
3
|
+
class PriorityQueue
|
4
|
+
# By default, the priority queue returns the maximum element first.
|
5
|
+
# If a block is given, the priority between the elements is determined with it.
|
6
|
+
# For example, the following block is given, the priority queue returns the minimum element first.
|
7
|
+
# `PriorityQueue.new { |x, y| x < y }`
|
8
|
+
#
|
9
|
+
# A heap is an array for which a[k] <= a[2*k+1] and a[k] <= a[2*k+2] for all k, counting elements from 0.
|
10
|
+
def initialize(array = [], &comp)
|
11
|
+
@heap = array
|
12
|
+
@comp = comp || proc { |x, y| x > y }
|
13
|
+
heapify
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :heap
|
17
|
+
|
18
|
+
# Push new element to the heap.
|
19
|
+
def push(item)
|
20
|
+
shift_down(0, @heap.push(item).size - 1)
|
21
|
+
end
|
22
|
+
|
23
|
+
alias << push
|
24
|
+
alias append push
|
25
|
+
|
26
|
+
# Pop the element with the highest priority.
|
27
|
+
def pop
|
28
|
+
latest = @heap.pop
|
29
|
+
return latest if empty?
|
30
|
+
|
31
|
+
ret_item = heap[0]
|
32
|
+
heap[0] = latest
|
33
|
+
shift_up(0)
|
34
|
+
ret_item
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get the element with the highest priority.
|
38
|
+
def get
|
39
|
+
@heap[0]
|
40
|
+
end
|
41
|
+
|
42
|
+
alias top get
|
43
|
+
|
44
|
+
# Returns true if the heap is empty.
|
45
|
+
def empty?
|
46
|
+
@heap.empty?
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def heapify
|
52
|
+
(@heap.size / 2 - 1).downto(0) { |i| shift_up(i) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def shift_up(pos)
|
56
|
+
end_pos = @heap.size
|
57
|
+
start_pos = pos
|
58
|
+
new_item = @heap[pos]
|
59
|
+
left_child_pos = 2 * pos + 1
|
60
|
+
|
61
|
+
while left_child_pos < end_pos
|
62
|
+
right_child_pos = left_child_pos + 1
|
63
|
+
if right_child_pos < end_pos && @comp.call(@heap[right_child_pos], @heap[left_child_pos])
|
64
|
+
left_child_pos = right_child_pos
|
65
|
+
end
|
66
|
+
# Move the higher priority child up.
|
67
|
+
@heap[pos] = @heap[left_child_pos]
|
68
|
+
pos = left_child_pos
|
69
|
+
left_child_pos = 2 * pos + 1
|
70
|
+
end
|
71
|
+
@heap[pos] = new_item
|
72
|
+
shift_down(start_pos, pos)
|
73
|
+
end
|
74
|
+
|
75
|
+
def shift_down(star_pos, pos)
|
76
|
+
new_item = @heap[pos]
|
77
|
+
while pos > star_pos
|
78
|
+
parent_pos = (pos - 1) >> 1
|
79
|
+
parent = @heap[parent_pos]
|
80
|
+
break if @comp.call(parent, new_item)
|
81
|
+
|
82
|
+
@heap[pos] = parent
|
83
|
+
pos = parent_pos
|
84
|
+
end
|
85
|
+
@heap[pos] = new_item
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
HeapQueue = PriorityQueue
|
data/lib/scc.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Strongly Connected Components
|
2
|
+
class SCCGraph
|
3
|
+
# initialize graph with n vertices
|
4
|
+
def initialize(n = 0)
|
5
|
+
@n, @edges = n, []
|
6
|
+
end
|
7
|
+
|
8
|
+
# add directed edge
|
9
|
+
def add_edge(from, to)
|
10
|
+
raise "invalid params" unless (0...@n).include? from and (0...@n).include? to
|
11
|
+
|
12
|
+
@edges << [from, to]
|
13
|
+
end
|
14
|
+
|
15
|
+
# returns list of strongly connected components
|
16
|
+
# the components are sorted in topological order
|
17
|
+
# O(@n + @edges.size)
|
18
|
+
def scc
|
19
|
+
group_num, ids = scc_ids
|
20
|
+
counts = [0] * group_num
|
21
|
+
ids.each { |x| counts[x] += 1 }
|
22
|
+
groups = Array.new(group_num) { [] }
|
23
|
+
ids.each_with_index { |x, i| groups[x] << i }
|
24
|
+
groups
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def scc_ids
|
30
|
+
start, elist = csr
|
31
|
+
now_ord = group_num = 0
|
32
|
+
visited, low, ord, ids = [], [], [-1] * @n, []
|
33
|
+
dfs = ->(v) {
|
34
|
+
low[v] = ord[v] = now_ord
|
35
|
+
now_ord += 1
|
36
|
+
visited << v
|
37
|
+
(start[v]...start[v + 1]).each do |i|
|
38
|
+
to = elist[i]
|
39
|
+
low[v] = if ord[to] == -1
|
40
|
+
dfs.(to)
|
41
|
+
[low[v], low[to]].min
|
42
|
+
else
|
43
|
+
[low[v], ord[to]].min
|
44
|
+
end
|
45
|
+
end
|
46
|
+
if low[v] == ord[v]
|
47
|
+
loop do
|
48
|
+
u = visited.pop
|
49
|
+
ord[u] = @n
|
50
|
+
ids[u] = group_num
|
51
|
+
break if u == v
|
52
|
+
end
|
53
|
+
group_num += 1
|
54
|
+
end
|
55
|
+
}
|
56
|
+
@n.times { |i| dfs.(i) if ord[i] == -1 }
|
57
|
+
ids = ids.map { |x| group_num - 1 - x }
|
58
|
+
[group_num, ids]
|
59
|
+
end
|
60
|
+
|
61
|
+
def csr
|
62
|
+
start, elist = [0] * (@n + 1), [nil] * @edges.size
|
63
|
+
@edges.each { |(i, _)| start[i + 1] += 1 }
|
64
|
+
@n.times { |i| start[i + 1] += start[i] }
|
65
|
+
counter = start.dup
|
66
|
+
@edges.each do |(i, j)|
|
67
|
+
elist[counter[i]] = j
|
68
|
+
counter[i] += 1
|
69
|
+
end
|
70
|
+
[start, elist]
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# class alias
|
75
|
+
StronglyConnectedComponents = SCCGraph
|
76
|
+
SCC = SCCGraph
|
77
|
+
SCCG = SCCGraph
|
data/lib/segtree.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# Segment Tree
|
2
|
+
class Segtree
|
3
|
+
attr_reader :d, :op, :n, :leaf_size, :log
|
4
|
+
|
5
|
+
def initialize(arg = 0, e, &block)
|
6
|
+
case arg
|
7
|
+
when Integer
|
8
|
+
v = Array.new(arg) { e }
|
9
|
+
when Array
|
10
|
+
v = arg
|
11
|
+
end
|
12
|
+
|
13
|
+
@e = e
|
14
|
+
@op = proc(&block)
|
15
|
+
|
16
|
+
@n = v.size
|
17
|
+
@log = (@n - 1).bit_length
|
18
|
+
@leaf_size = 1 << @log
|
19
|
+
@d = Array.new(@leaf_size * 2) { e }
|
20
|
+
v.each_with_index { |v_i, i| @d[@leaf_size + i] = v_i }
|
21
|
+
(@leaf_size - 1).downto(1) { |i| update(i) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def set(q, x)
|
25
|
+
q += @leaf_size
|
26
|
+
@d[q] = x
|
27
|
+
1.upto(@log) { |i| update(q >> i) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def get(pos)
|
31
|
+
@d[@leaf_size + pos]
|
32
|
+
end
|
33
|
+
|
34
|
+
def prod(l, r)
|
35
|
+
return @e if l == r
|
36
|
+
|
37
|
+
sml = @e
|
38
|
+
smr = @e
|
39
|
+
l += @leaf_size
|
40
|
+
r += @leaf_size
|
41
|
+
|
42
|
+
while l < r
|
43
|
+
if l[0] == 1
|
44
|
+
sml = @op.call(sml, @d[l])
|
45
|
+
l += 1
|
46
|
+
end
|
47
|
+
if r[0] == 1
|
48
|
+
r -= 1
|
49
|
+
smr = @op.call(@d[r], smr)
|
50
|
+
end
|
51
|
+
l /= 2
|
52
|
+
r /= 2
|
53
|
+
end
|
54
|
+
|
55
|
+
@op.call(sml, smr)
|
56
|
+
end
|
57
|
+
|
58
|
+
def all_prod
|
59
|
+
@d[1]
|
60
|
+
end
|
61
|
+
|
62
|
+
def max_right(l, &block)
|
63
|
+
return @n if l == @n
|
64
|
+
|
65
|
+
f = proc(&block)
|
66
|
+
|
67
|
+
l += @leaf_size
|
68
|
+
sm = @e
|
69
|
+
loop do
|
70
|
+
l /= 2 while l.even?
|
71
|
+
unless f.call(@op.call(sm, @d[l]))
|
72
|
+
while l < @leaf_size
|
73
|
+
l *= 2
|
74
|
+
if f.call(@op.call(sm, @d[l]))
|
75
|
+
sm = @op.call(sm, @d[l])
|
76
|
+
l += 1
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
return l - @leaf_size
|
81
|
+
end
|
82
|
+
|
83
|
+
sm = @op.call(sm, @d[l])
|
84
|
+
l += 1
|
85
|
+
break if (l & -l) == l
|
86
|
+
end
|
87
|
+
|
88
|
+
@n
|
89
|
+
end
|
90
|
+
|
91
|
+
def min_left(r, &block)
|
92
|
+
return 0 if r == 0
|
93
|
+
|
94
|
+
f = proc(&block)
|
95
|
+
|
96
|
+
r += @leaf_size
|
97
|
+
sm = @e
|
98
|
+
loop do
|
99
|
+
r -= 1
|
100
|
+
r /= 2 while r > 1 && r.odd?
|
101
|
+
unless f.call(@op.call(@d[r], sm))
|
102
|
+
while r < @leaf_size
|
103
|
+
r = r * 2 + 1
|
104
|
+
if f.call(@op.call(@d[r], sm))
|
105
|
+
sm = @op.call(@d[r], sm)
|
106
|
+
r -= 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
return r + 1 - @leaf_size
|
111
|
+
end
|
112
|
+
|
113
|
+
sm = @op.call(@d[r], sm)
|
114
|
+
break if (r & -r) == r
|
115
|
+
end
|
116
|
+
|
117
|
+
0
|
118
|
+
end
|
119
|
+
|
120
|
+
def update(k)
|
121
|
+
@d[k] = @op.call(@d[2 * k], @d[2 * k + 1])
|
122
|
+
end
|
123
|
+
|
124
|
+
def inspect
|
125
|
+
t = 0
|
126
|
+
res = "SegmentTree @e = #{@e}, @n = #{@n}, @leaf_size = #{@leaf_size} @op = #{@op}\n "
|
127
|
+
a = @d[1, @d.size - 1]
|
128
|
+
a.each_with_index do |e, i|
|
129
|
+
res << e.to_s << ' '
|
130
|
+
if t == i && i < @leaf_size
|
131
|
+
res << "\n "
|
132
|
+
t = t * 2 + 2
|
133
|
+
end
|
134
|
+
end
|
135
|
+
res
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
SegTree = Segtree
|
140
|
+
SegmentTree = Segtree
|
data/lib/suffix_array.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# induce sort (internal method)
|
2
|
+
def sa_is_induce(s, ls, sum_l, sum_s, lms)
|
3
|
+
n = s.size
|
4
|
+
sa = [-1] * n
|
5
|
+
|
6
|
+
buf = sum_s.dup
|
7
|
+
lms.each{ |lms|
|
8
|
+
if lms != n
|
9
|
+
sa[buf[s[lms]]] = lms
|
10
|
+
buf[s[lms]] += 1
|
11
|
+
end
|
12
|
+
}
|
13
|
+
|
14
|
+
buf = sum_l.dup
|
15
|
+
sa[buf[s[-1]]] = n - 1
|
16
|
+
buf[s[-1]] += 1
|
17
|
+
sa.each{ |v|
|
18
|
+
if v >= 1 && !ls[v - 1]
|
19
|
+
sa[buf[s[v - 1]]] = v - 1
|
20
|
+
buf[s[v - 1]] += 1
|
21
|
+
end
|
22
|
+
}
|
23
|
+
|
24
|
+
buf = sum_l.dup
|
25
|
+
sa.reverse_each{ |v|
|
26
|
+
if v >= 1 && ls[v - 1]
|
27
|
+
buf[s[v - 1] + 1] -= 1
|
28
|
+
sa[buf[s[v - 1] + 1]] = v - 1
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
return sa
|
33
|
+
end
|
34
|
+
|
35
|
+
# SA-IS (internal method)
|
36
|
+
def sa_is(s, upper)
|
37
|
+
n = s.size
|
38
|
+
|
39
|
+
return [] if n == 0
|
40
|
+
return [0] if n == 1
|
41
|
+
|
42
|
+
ls = [false] * n
|
43
|
+
(n - 2).downto(0){ |i|
|
44
|
+
ls[i] = (s[i] == s[i + 1] ? ls[i + 1] : s[i] < s[i + 1])
|
45
|
+
}
|
46
|
+
|
47
|
+
sum_l = [0] * (upper + 1)
|
48
|
+
sum_s = [0] * (upper + 1)
|
49
|
+
n.times{ |i|
|
50
|
+
if ls[i]
|
51
|
+
sum_l[s[i] + 1] += 1
|
52
|
+
else
|
53
|
+
sum_s[s[i]] += 1
|
54
|
+
end
|
55
|
+
}
|
56
|
+
0.upto(upper){ |i|
|
57
|
+
sum_s[i] += sum_l[i]
|
58
|
+
sum_l[i + 1] += sum_s[i] if i < upper
|
59
|
+
}
|
60
|
+
|
61
|
+
lms = (1 ... n).select{ |i| !ls[i - 1] && ls[i] }
|
62
|
+
m = lms.size
|
63
|
+
lms_map = [-1] * (n + 1)
|
64
|
+
lms.each_with_index{ |lms, id| lms_map[lms] = id }
|
65
|
+
|
66
|
+
sa = sa_is_induce(s, ls, sum_l, sum_s, lms)
|
67
|
+
|
68
|
+
return sa if m == 0
|
69
|
+
|
70
|
+
sorted_lms = sa.select{ |sa| lms_map[sa] != -1 }
|
71
|
+
rec_s = [0] * m
|
72
|
+
rec_upper = 0
|
73
|
+
rec_s[lms_map[sorted_lms[0]]] = 0
|
74
|
+
1.upto(m - 1) do |i|
|
75
|
+
l, r = sorted_lms[i - 1, 2]
|
76
|
+
end_l = lms[lms_map[l] + 1] || n
|
77
|
+
end_r = lms[lms_map[r] + 1] || n
|
78
|
+
same = true
|
79
|
+
if end_l - l != end_r - r
|
80
|
+
same = false
|
81
|
+
else
|
82
|
+
while l < end_l
|
83
|
+
break if s[l] != s[r]
|
84
|
+
|
85
|
+
l += 1
|
86
|
+
r += 1
|
87
|
+
end
|
88
|
+
same = false if l == n || s[l] != s[r]
|
89
|
+
end
|
90
|
+
rec_upper += 1 if not same
|
91
|
+
rec_s[lms_map[sorted_lms[i]]] = rec_upper
|
92
|
+
end
|
93
|
+
|
94
|
+
sa_is(rec_s, rec_upper).each_with_index{ |rec_sa, id|
|
95
|
+
sorted_lms[id] = lms[rec_sa]
|
96
|
+
}
|
97
|
+
|
98
|
+
return sa_is_induce(s, ls, sum_l, sum_s, sorted_lms)
|
99
|
+
end
|
100
|
+
|
101
|
+
# suffix array for array of integers or string
|
102
|
+
def suffix_array(s, upper = nil)
|
103
|
+
if not upper
|
104
|
+
case s
|
105
|
+
when Array
|
106
|
+
# compression
|
107
|
+
n = s.size
|
108
|
+
idx = (0 ... n).sort_by{ |i| s[i] }
|
109
|
+
t = [0] * n
|
110
|
+
upper = 0
|
111
|
+
t[idx[0]] = 0
|
112
|
+
1.upto(n - 1){ |i|
|
113
|
+
upper += 1 if s[idx[i - 1]] != s[idx[i]]
|
114
|
+
t[idx[i]] = upper
|
115
|
+
}
|
116
|
+
s = t
|
117
|
+
when String
|
118
|
+
upper = 255
|
119
|
+
s = s.bytes
|
120
|
+
end
|
121
|
+
else
|
122
|
+
s.each{ |s|
|
123
|
+
raise ArgumentError if s < 0 || upper < s
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
return sa_is(s, upper)
|
128
|
+
end
|