|
1 | 1 | package g3201_3300.s3245_alternating_groups_iii;
|
2 | 2 |
|
3 |
| -// #Hard #Array #Binary_Indexed_Tree #2024_08_06_Time_36_ms_(82.22%)_Space_70.3_MB_(97.78%) |
| 3 | +// #Hard #Array #Binary_Indexed_Tree #2025_02_12_Time_135_ms_(86.36%)_Space_84.24_MB_(40.91%) |
4 | 4 |
|
5 | 5 | import java.util.ArrayList;
|
6 | 6 | import java.util.List;
|
| 7 | +import java.util.Map; |
| 8 | +import java.util.TreeMap; |
7 | 9 |
|
8 | 10 | public class Solution {
|
9 |
| - private void go(int ind, LST lst, int[] fs, int n, LST ff, int[] c) { |
10 |
| - if (ind > 0) { |
11 |
| - int pre = lst.prev(ind - 1); |
12 |
| - int nex = lst.next(pre + 1); |
13 |
| - if (nex == -1) { |
14 |
| - nex = 2 * n; |
15 |
| - } |
16 |
| - if (pre != -1 && pre < n && --fs[nex - pre] == 0) { |
17 |
| - ff.unsetPos(nex - pre); |
18 |
| - } |
19 |
| - } |
20 |
| - if (lst.get(ind)) { |
21 |
| - int pre = ind; |
22 |
| - int nex = lst.next(ind + 1); |
23 |
| - if (nex == -1) { |
24 |
| - nex = 2 * n; |
25 |
| - } |
26 |
| - if (pre != -1 && pre < n && --fs[nex - pre] == 0) { |
27 |
| - ff.unsetPos(nex - pre); |
28 |
| - } |
29 |
| - } |
30 |
| - if (lst.get(ind + 1)) { |
31 |
| - int pre = ind + 1; |
32 |
| - int nex = lst.next(ind + 2); |
33 |
| - if (nex == -1) { |
34 |
| - nex = 2 * n; |
35 |
| - } |
36 |
| - if (pre != -1 && pre < n && --fs[nex - pre] == 0) { |
37 |
| - ff.unsetPos(nex - pre); |
38 |
| - } |
39 |
| - } |
40 |
| - lst.unsetPos(ind); |
41 |
| - lst.unsetPos(ind + 1); |
42 |
| - c[ind] ^= 1; |
43 |
| - if (ind > 0 && c[ind] != c[ind - 1]) { |
44 |
| - lst.setPos(ind); |
45 |
| - } |
46 |
| - if (ind + 1 < c.length && c[ind + 1] != c[ind]) { |
47 |
| - lst.setPos(ind + 1); |
48 |
| - } |
49 |
| - if (ind > 0) { |
50 |
| - int pre = lst.prev(ind - 1); |
51 |
| - int nex = lst.next(pre + 1); |
52 |
| - if (nex == -1) { |
53 |
| - nex = 2 * n; |
54 |
| - } |
55 |
| - if (pre != -1 && pre < n && ++fs[nex - pre] == 1) { |
56 |
| - ff.setPos(nex - pre); |
| 11 | + private static final int SZ = 63333; |
| 12 | + private static final int OFFSET = SZ - 10; |
| 13 | + private static final BIT[] BITS = {new BIT(), new BIT()}; |
| 14 | + |
| 15 | + // Class to represent the Binary Indexed Tree (BIT) |
| 16 | + private static class BIT { |
| 17 | + int[] bs = new int[SZ]; |
| 18 | + |
| 19 | + // Update BIT: add value y to index x |
| 20 | + void update(int x, int y) { |
| 21 | + x = OFFSET - x; |
| 22 | + for (; x < SZ; x += x & -x) { |
| 23 | + bs[x] += y; |
57 | 24 | }
|
58 | 25 | }
|
59 |
| - if (lst.get(ind)) { |
60 |
| - int pre = ind; |
61 |
| - int nex = lst.next(ind + 1); |
62 |
| - if (nex == -1) { |
63 |
| - nex = 2 * n; |
64 |
| - } |
65 |
| - if (pre < n && ++fs[nex - pre] == 1) { |
66 |
| - ff.setPos(nex - pre); |
| 26 | + |
| 27 | + // Query BIT: get the prefix sum up to index x |
| 28 | + int query(int x) { |
| 29 | + x = OFFSET - x; |
| 30 | + int ans = 0; |
| 31 | + for (; x > 0; x -= x & -x) { |
| 32 | + ans += bs[x]; |
67 | 33 | }
|
| 34 | + return ans; |
68 | 35 | }
|
69 |
| - if (lst.get(ind + 1)) { |
70 |
| - int pre = ind + 1; |
71 |
| - int nex = lst.next(ind + 2); |
72 |
| - if (nex == -1) { |
73 |
| - nex = 2 * n; |
74 |
| - } |
75 |
| - if (pre < n && ++fs[nex - pre] == 1) { |
76 |
| - ff.setPos(nex - pre); |
| 36 | + |
| 37 | + // Clear BIT values up to index x |
| 38 | + void clear(int x) { |
| 39 | + x = OFFSET - x; |
| 40 | + for (; x < SZ; x += x & -x) { |
| 41 | + bs[x] = 0; |
77 | 42 | }
|
78 | 43 | }
|
79 | 44 | }
|
80 | 45 |
|
| 46 | + // Wrapper functions for updating and querying the BITs |
| 47 | + private void edt(int x, int y) { |
| 48 | + // Update second BIT with product of index and value |
| 49 | + BITS[1].update(x, x * y); |
| 50 | + // Update first BIT with value |
| 51 | + BITS[0].update(x, y); |
| 52 | + } |
| 53 | + |
| 54 | + private int qry(int x) { |
| 55 | + // Query BITs and combine results |
| 56 | + return BITS[1].query(x) + (1 - x) * BITS[0].query(x); |
| 57 | + } |
| 58 | + |
| 59 | + // Function to calculate the length between two indices |
| 60 | + private int len(int x, int y) { |
| 61 | + return y - x + 1; |
| 62 | + } |
| 63 | + |
| 64 | + // Main function to handle the queries |
81 | 65 | public List<Integer> numberOfAlternatingGroups(int[] colors, int[][] queries) {
|
| 66 | + // Map to store start and end indices of alternating groups |
| 67 | + TreeMap<Integer, Integer> c = new TreeMap<>(); |
82 | 68 | int n = colors.length;
|
83 |
| - int[] c = new int[2 * n]; |
84 |
| - for (int i = 0; i < 2 * n; i++) { |
85 |
| - c[i] = colors[i % n] ^ (i % 2 == 0 ? 0 : 1); |
86 |
| - } |
87 |
| - LST lst = new LST(2 * n + 3); |
88 |
| - for (int i = 1; i < 2 * n; i++) { |
89 |
| - if (c[i] != c[i - 1]) { |
90 |
| - lst.setPos(i); |
91 |
| - } |
92 |
| - } |
93 |
| - int[] fs = new int[2 * n + 1]; |
94 |
| - LST ff = new LST(2 * n + 1); |
95 |
| - for (int i = 0; i < n; i++) { |
96 |
| - if (lst.get(i)) { |
97 |
| - int ne = lst.next(i + 1); |
98 |
| - if (ne == -1) { |
99 |
| - ne = 2 * n; |
100 |
| - } |
101 |
| - fs[ne - i]++; |
102 |
| - ff.setPos(ne - i); |
103 |
| - } |
104 |
| - } |
105 |
| - List<Integer> ans = new ArrayList<>(); |
| 69 | + // Initialize alternating groups |
| 70 | + for (int i = 0; i < colors.length; ++i) { |
| 71 | + int r = i; |
| 72 | + // Find end of the current alternating group |
| 73 | + while (r < colors.length && (colors[r] + colors[i] + r + i) % 2 == 0) { |
| 74 | + ++r; |
| 75 | + } |
| 76 | + // Store group boundaries in map |
| 77 | + c.put(i, r - 1); |
| 78 | + // Update BITs with new group |
| 79 | + edt(r - i, 1); |
| 80 | + // Move to the end of the current group |
| 81 | + i = r - 1; |
| 82 | + } |
| 83 | + // List to store results for type 1 queries |
| 84 | + List<Integer> results = new ArrayList<>(); |
| 85 | + // Process each query |
106 | 86 | for (int[] q : queries) {
|
107 | 87 | if (q[0] == 1) {
|
108 |
| - if (lst.next(0) == -1) { |
109 |
| - ans.add(n); |
110 |
| - } else { |
111 |
| - int lans = 0; |
112 |
| - for (int i = ff.next(q[1]); i != -1; i = ff.next(i + 1)) { |
113 |
| - lans += (i - q[1] + 1) * fs[i]; |
| 88 | + // Query type 1: Count alternating groups of a given size |
| 89 | + int ans = qry(q[1]); |
| 90 | + Map.Entry<Integer, Integer> a = c.firstEntry(); |
| 91 | + Map.Entry<Integer, Integer> b = c.lastEntry(); |
| 92 | + if (a != b) { |
| 93 | + // Check if merging groups is possible |
| 94 | + if (colors[0] != colors[colors.length - 1]) { |
| 95 | + int l1 = len(a.getKey(), a.getValue()); |
| 96 | + int l2 = len(b.getKey(), b.getValue()); |
| 97 | + // Subtract groups that are too small |
| 98 | + ans -= Math.max(l1 - q[1] + 1, 0); |
| 99 | + ans -= Math.max(l2 - q[1] + 1, 0); |
| 100 | + // Add merged group size |
| 101 | + ans += Math.max(l1 + l2 - q[1] + 1, 0); |
114 | 102 | }
|
115 |
| - if (c[2 * n - 1] != c[0]) { |
116 |
| - int f = lst.next(0); |
117 |
| - if (f >= q[1]) { |
118 |
| - lans += (f - q[1] + 1); |
119 |
| - } |
120 |
| - } |
121 |
| - ans.add(lans); |
| 103 | + } else if (colors[0] != colors[colors.length - 1]) { |
| 104 | + // If there's only one group, check if it can span the entire array |
| 105 | + ans = n; |
122 | 106 | }
|
| 107 | + // Store result for type 1 query |
| 108 | + results.add(ans); |
123 | 109 | } else {
|
124 |
| - int ind = q[1]; |
125 |
| - int val = q[2]; |
126 |
| - if (colors[ind] == val) { |
| 110 | + // Query type 2: Update color at a given index |
| 111 | + int x = q[1]; |
| 112 | + int y = q[2]; |
| 113 | + if (colors[x] == y) { |
| 114 | + // If color is already correct, skip update |
127 | 115 | continue;
|
128 | 116 | }
|
129 |
| - colors[ind] ^= 1; |
130 |
| - go(ind, lst, fs, n, ff, c); |
131 |
| - go(ind + n, lst, fs, n, ff, c); |
132 |
| - } |
133 |
| - } |
134 |
| - return ans; |
135 |
| - } |
136 |
| - |
137 |
| - private static class LST { |
138 |
| - private long[][] set; |
139 |
| - private int n; |
140 |
| - |
141 |
| - public LST(int n) { |
142 |
| - this.n = n; |
143 |
| - int d = 1; |
144 |
| - d = getD(n, d); |
145 |
| - set = new long[d][]; |
146 |
| - for (int i = 0, m = n >>> 6; i < d; i++, m >>>= 6) { |
147 |
| - set[i] = new long[m + 1]; |
148 |
| - } |
149 |
| - } |
150 |
| - |
151 |
| - private int getD(int n, int d) { |
152 |
| - int m = n; |
153 |
| - while (m > 1) { |
154 |
| - m >>>= 6; |
155 |
| - d++; |
156 |
| - } |
157 |
| - return d; |
158 |
| - } |
159 |
| - |
160 |
| - public LST setPos(int pos) { |
161 |
| - if (pos >= 0 && pos < n) { |
162 |
| - for (int i = 0; i < set.length; i++, pos >>>= 6) { |
163 |
| - set[i][pos >>> 6] |= 1L << pos; |
164 |
| - } |
165 |
| - } |
166 |
| - return this; |
167 |
| - } |
168 |
| - |
169 |
| - public LST unsetPos(int pos) { |
170 |
| - if (pos >= 0 && pos < n) { |
171 |
| - for (int i = 0; |
172 |
| - i < set.length && (i == 0 || set[i - 1][pos] == 0L); |
173 |
| - i++, pos >>>= 6) { |
174 |
| - set[i][pos >>> 6] &= ~(1L << pos); |
175 |
| - } |
176 |
| - } |
177 |
| - return this; |
178 |
| - } |
179 |
| - |
180 |
| - public boolean get(int pos) { |
181 |
| - return pos >= 0 && pos < n && set[0][pos >>> 6] << ~pos < 0; |
182 |
| - } |
183 |
| - |
184 |
| - public int prev(int pos) { |
185 |
| - int i = 0; |
186 |
| - while (i < set.length && pos >= 0) { |
187 |
| - int pre = prev(set[i][pos >>> 6], pos & 63); |
188 |
| - if (pre != -1) { |
189 |
| - pos = pos >>> 6 << 6 | pre; |
190 |
| - while (i > 0) { |
191 |
| - pos = pos << 6 | 63 - Long.numberOfLeadingZeros(set[--i][pos]); |
| 117 | + // Update color |
| 118 | + colors[x] = y; |
| 119 | + // Find the block containing index x |
| 120 | + Map.Entry<Integer, Integer> it = c.floorEntry(x); |
| 121 | + assert it != null && it.getKey() <= x && it.getValue() >= x; |
| 122 | + int l = it.getKey(); |
| 123 | + int r = it.getValue(); |
| 124 | + // Remove the old block |
| 125 | + edt(len(it.getKey(), it.getValue()), -1); |
| 126 | + c.remove(it.getKey()); |
| 127 | + int ml = x; |
| 128 | + int mr = x; |
| 129 | + // Update or split the affected blocks |
| 130 | + if (l != ml) { |
| 131 | + c.put(l, x - 1); |
| 132 | + edt(len(l, x - 1), 1); |
| 133 | + } else { |
| 134 | + if (x > 0 && colors[x] != colors[x - 1]) { |
| 135 | + it = c.floorEntry(x - 1); |
| 136 | + if (it != null) { |
| 137 | + ml = it.getKey(); |
| 138 | + edt(len(it.getKey(), it.getValue()), -1); |
| 139 | + c.remove(it.getKey()); |
| 140 | + } |
192 | 141 | }
|
193 |
| - return pos; |
194 | 142 | }
|
195 |
| - i++; |
196 |
| - pos >>>= 6; |
197 |
| - pos--; |
198 |
| - } |
199 |
| - return -1; |
200 |
| - } |
201 |
| - |
202 |
| - private int prev(long set, int n) { |
203 |
| - long h = set << ~n; |
204 |
| - if (h == 0L) { |
205 |
| - return -1; |
206 |
| - } |
207 |
| - return -Long.numberOfLeadingZeros(h) + n; |
208 |
| - } |
209 |
| - |
210 |
| - public int next(int pos) { |
211 |
| - int i = 0; |
212 |
| - while (i < set.length && pos >>> 6 < set[i].length) { |
213 |
| - int nex = next(set[i][pos >>> 6], pos & 63); |
214 |
| - if (nex != -1) { |
215 |
| - pos = pos >>> 6 << 6 | nex; |
216 |
| - while (i > 0) { |
217 |
| - pos = pos << 6 | Long.numberOfTrailingZeros(set[--i][pos]); |
| 143 | + if (r != mr) { |
| 144 | + c.put(x + 1, r); |
| 145 | + edt(len(x + 1, r), 1); |
| 146 | + } else { |
| 147 | + if (x + 1 < colors.length && colors[x + 1] != colors[x]) { |
| 148 | + it = c.ceilingEntry(x + 1); |
| 149 | + if (it != null) { |
| 150 | + mr = it.getValue(); |
| 151 | + edt(len(it.getKey(), it.getValue()), -1); |
| 152 | + c.remove(it.getKey()); |
| 153 | + } |
218 | 154 | }
|
219 |
| - return pos; |
220 | 155 | }
|
221 |
| - i++; |
222 |
| - pos >>>= 6; |
223 |
| - pos++; |
224 |
| - } |
225 |
| - return -1; |
226 |
| - } |
227 |
| - |
228 |
| - private static int next(long set, int n) { |
229 |
| - long h = set >>> n; |
230 |
| - if (h == 0L) { |
231 |
| - return -1; |
| 156 | + c.put(ml, mr); |
| 157 | + // Add new or modified block |
| 158 | + edt(len(ml, mr), 1); |
232 | 159 | }
|
233 |
| - return Long.numberOfTrailingZeros(h) + n; |
234 | 160 | }
|
235 |
| - |
236 |
| - @Override |
237 |
| - public String toString() { |
238 |
| - List<Integer> list = new ArrayList<>(); |
239 |
| - for (int pos = next(0); pos != -1; pos = next(pos + 1)) { |
240 |
| - list.add(pos); |
241 |
| - } |
242 |
| - return list.toString(); |
| 161 | + // Clear BITs after processing all queries |
| 162 | + for (int i = 0; i <= n + 2; ++i) { |
| 163 | + BITS[0].clear(i); |
| 164 | + BITS[1].clear(i); |
243 | 165 | }
|
| 166 | + return results; |
244 | 167 | }
|
245 | 168 | }
|
0 commit comments