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.
Files changed (84) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/unittest.yml +16 -0
  3. data/.gitignore +11 -0
  4. data/.rubocop.yml +198 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +116 -0
  7. data/README.ja.md +56 -0
  8. data/README.md +41 -0
  9. data/Rakefile +11 -0
  10. data/ac-library-rb.gemspec +32 -0
  11. data/bin/console +8 -0
  12. data/bin/lock_lib.rb +27 -0
  13. data/bin/setup +8 -0
  14. data/document_en/binary_index_tree.md +3 -0
  15. data/document_en/convolution.md +67 -0
  16. data/document_en/dsu.md +132 -0
  17. data/document_en/fenwick_tree.md +99 -0
  18. data/document_en/index.md +79 -0
  19. data/document_en/lazy_segtree.md +141 -0
  20. data/document_en/math.md +104 -0
  21. data/document_en/max_flow.md +165 -0
  22. data/document_en/min_cost_flow.md +132 -0
  23. data/document_en/modint.md +263 -0
  24. data/document_en/priority_queue.md +119 -0
  25. data/document_en/segtree.md +134 -0
  26. data/document_en/string.md +106 -0
  27. data/document_en/two_sat.md +91 -0
  28. data/document_en/union_find.md +3 -0
  29. data/document_ja/convolution.md +64 -0
  30. data/document_ja/dsu.md +183 -0
  31. data/document_ja/fenwick_tree.md +83 -0
  32. data/document_ja/index.md +89 -0
  33. data/document_ja/lazy_segtree.md +135 -0
  34. data/document_ja/math.md +116 -0
  35. data/document_ja/max_flow.md +129 -0
  36. data/document_ja/min_cost_flow.md +105 -0
  37. data/document_ja/modint.md +349 -0
  38. data/document_ja/priority_queue.md +103 -0
  39. data/document_ja/scc.md +65 -0
  40. data/document_ja/segtree.md +145 -0
  41. data/document_ja/string.md +105 -0
  42. data/document_ja/two_sat.md +87 -0
  43. data/lib/ac-library-rb/version.rb +3 -0
  44. data/lib/convolution.rb +124 -0
  45. data/lib/core_ext/modint.rb +19 -0
  46. data/lib/crt.rb +52 -0
  47. data/lib/dsu.rb +44 -0
  48. data/lib/fenwick_tree.rb +48 -0
  49. data/lib/floor_sum.rb +21 -0
  50. data/lib/inv_mod.rb +26 -0
  51. data/lib/lazy_segtree.rb +149 -0
  52. data/lib/lcp_array.rb +23 -0
  53. data/lib/max_flow.rb +137 -0
  54. data/lib/min_cost_flow.rb +143 -0
  55. data/lib/modint.rb +170 -0
  56. data/lib/pow_mod.rb +13 -0
  57. data/lib/priority_queue.rb +89 -0
  58. data/lib/scc.rb +77 -0
  59. data/lib/segtree.rb +140 -0
  60. data/lib/suffix_array.rb +128 -0
  61. data/lib/two_sat.rb +34 -0
  62. data/lib/z_algorithm.rb +32 -0
  63. data/lib_helpers/ac-library-rb/all.rb +22 -0
  64. data/lib_lock/ac-library-rb.rb +22 -0
  65. data/lib_lock/ac-library-rb/convolution.rb +126 -0
  66. data/lib_lock/ac-library-rb/core_ext/modint.rb +19 -0
  67. data/lib_lock/ac-library-rb/crt.rb +54 -0
  68. data/lib_lock/ac-library-rb/dsu.rb +46 -0
  69. data/lib_lock/ac-library-rb/fenwick_tree.rb +50 -0
  70. data/lib_lock/ac-library-rb/floor_sum.rb +23 -0
  71. data/lib_lock/ac-library-rb/inv_mod.rb +28 -0
  72. data/lib_lock/ac-library-rb/lazy_segtree.rb +151 -0
  73. data/lib_lock/ac-library-rb/lcp_array.rb +25 -0
  74. data/lib_lock/ac-library-rb/max_flow.rb +139 -0
  75. data/lib_lock/ac-library-rb/min_cost_flow.rb +145 -0
  76. data/lib_lock/ac-library-rb/modint.rb +172 -0
  77. data/lib_lock/ac-library-rb/pow_mod.rb +15 -0
  78. data/lib_lock/ac-library-rb/priority_queue.rb +91 -0
  79. data/lib_lock/ac-library-rb/scc.rb +79 -0
  80. data/lib_lock/ac-library-rb/segtree.rb +142 -0
  81. data/lib_lock/ac-library-rb/suffix_array.rb +130 -0
  82. data/lib_lock/ac-library-rb/two_sat.rb +36 -0
  83. data/lib_lock/ac-library-rb/z_algorithm.rb +34 -0
  84. metadata +158 -0
data/lib/pow_mod.rb ADDED
@@ -0,0 +1,13 @@
1
+ # Use `Integer#pow` unless m == 1
2
+ def pow_mod(x, n, m)
3
+ return 0 if m == 1
4
+
5
+ r, y = 1, x % m
6
+ while n > 0
7
+ r = r * y % m if n.odd?
8
+ y = y * y % m
9
+ n >>= 1
10
+ end
11
+
12
+ r
13
+ end
@@ -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
@@ -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