|
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