ac-library-rb 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|