@@ -12,19 +12,19 @@ public class Solution {
1212 private static final int OFFSET = SZ - 10 ;
1313 private static final BIT [] BITS = {new BIT (), new BIT ()};
1414
15- // Class to represent the Binary Indexed Tree (BIT)
15+ // Binary Indexed Tree (BIT) class.
1616 private static class BIT {
1717 int [] bs = new int [SZ ];
1818
19- // Update BIT: add value y to index x
19+ // Update BIT: add value y to index x.
2020 void update (int x , int y ) {
2121 x = OFFSET - x ;
2222 for (; x < SZ ; x += x & -x ) {
2323 bs [x ] += y ;
2424 }
2525 }
2626
27- // Query BIT: get the prefix sum up to index x
27+ // Query BIT: get the prefix sum up to index x.
2828 int query (int x ) {
2929 x = OFFSET - x ;
3030 int ans = 0 ;
@@ -34,7 +34,7 @@ int query(int x) {
3434 return ans ;
3535 }
3636
37- // Clear BIT values up to index x
37+ // Clear BIT values starting from index x.
3838 void clear (int x ) {
3939 x = OFFSET - x ;
4040 for (; x < SZ ; x += x & -x ) {
@@ -43,126 +43,162 @@ void clear(int x) {
4343 }
4444 }
4545
46- // Wrapper functions for updating and querying the BITs
46+ // --- BIT wrapper methods ---
47+ // Updates both BITs for a given group length.
4748 private void edt (int x , int y ) {
48- // Update second BIT with product of index and value
49+ // Second BIT is updated with x * y.
4950 BITS [1 ].update (x , x * y );
50- // Update first BIT with value
51+ // First BIT is updated with y.
5152 BITS [0 ].update (x , y );
5253 }
5354
55+ // Combines BIT queries to get the result for a given x.
5456 private int qry (int x ) {
55- // Query BITs and combine results
5657 return BITS [1 ].query (x ) + (1 - x ) * BITS [0 ].query (x );
5758 }
5859
59- // Function to calculate the length between two indices
60+ // Returns the length of a group from index x to y.
6061 private int len (int x , int y ) {
6162 return y - x + 1 ;
6263 }
6364
64- // Main function to handle the queries
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 <>();
65+ // --- Group operations ---
66+ // Removes a group (block) by updating BIT with a negative value.
67+ private void removeGroup (int start , int end ) {
68+ edt (len (start , end ), -1 );
69+ }
70+
71+ // Adds a group (block) by updating BIT with a positive value.
72+ private void addGroup (int start , int end ) {
73+ edt (len (start , end ), 1 );
74+ }
75+
76+ // Initializes the alternating groups using the colors array.
77+ private void initializeGroups (int [] colors , TreeMap <Integer , Integer > groups ) {
6878 int n = colors .length ;
69- // Initialize alternating groups
70- for ( int i = 0 ; i < colors . length ; ++ i ) {
79+ int i = 0 ;
80+ while ( i < n ) {
7181 int r = i ;
72- // Find end of the current alternating group
73- while (r < colors . length && (colors [r ] + colors [i ] + r + i ) % 2 == 0 ) {
82+ // Determine the end of the current alternating group.
83+ while (r < n && (colors [r ] + colors [i ] + r + i ) % 2 == 0 ) {
7484 ++r ;
7585 }
76- // Store group boundaries in map
77- c .put (i , r - 1 );
78- // Update BITs with new group
86+ // The group spans from index i to r-1.
87+ groups .put (i , r - 1 );
88+ // Update BITs with the length of this group.
7989 edt (r - i , 1 );
80- // Move to the end of the current group
90+ // Skip to the end of the current group.
8191 i = r - 1 ;
92+ ++i ;
8293 }
83- // List to store results for type 1 queries
84- List <Integer > results = new ArrayList <>();
85- // Process each query
86- for (int [] q : queries ) {
87- if (q [0 ] == 1 ) {
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 );
102- }
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 ;
106- }
107- // Store result for type 1 query
108- results .add (ans );
109- } else {
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
115- continue ;
116- }
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- }
141- }
94+ }
95+
96+ // Processes a type 1 query: returns the number of alternating groups
97+ // of at least the given size.
98+ private int processQueryType1 (int [] colors , TreeMap <Integer , Integer > groups , int groupSize ) {
99+ int ans = qry (groupSize );
100+ Map .Entry <Integer , Integer > firstGroup = groups .firstEntry ();
101+ Map .Entry <Integer , Integer > lastGroup = groups .lastEntry ();
102+ // If there is more than one group and the first and last colors differ,
103+ // adjust the answer by "merging" the groups at the boundaries.
104+ if (firstGroup != lastGroup ) {
105+ if (colors [0 ] != colors [colors .length - 1 ]) {
106+ int leftLen = len (firstGroup .getKey (), firstGroup .getValue ());
107+ int rightLen = len (lastGroup .getKey (), lastGroup .getValue ());
108+ ans -= Math .max (leftLen - groupSize + 1 , 0 );
109+ ans -= Math .max (rightLen - groupSize + 1 , 0 );
110+ ans += Math .max (leftLen + rightLen - groupSize + 1 , 0 );
111+ }
112+ } else if (colors [0 ] != colors [colors .length - 1 ]) {
113+ // In the special case when there's a single group but the
114+ // first and last colors differ, the whole array is counted.
115+ ans = colors .length ;
116+ }
117+ return ans ;
118+ }
119+
120+ // Processes a type 2 query: updates the color at index x and adjusts groups.
121+ private void processQueryType2 (
122+ int [] colors , TreeMap <Integer , Integer > groups , int x , int newColor ) {
123+ if (colors [x ] == newColor ) {
124+ return ;
125+ }
126+ // Update the color at index x.
127+ colors [x ] = newColor ;
128+ // Find the group (block) that contains index x.
129+ Map .Entry <Integer , Integer > it = groups .floorEntry (x );
130+ int l = it .getKey ();
131+ int r = it .getValue ();
132+ // Remove the old group from BIT and map.
133+ removeGroup (l , r );
134+ groups .remove (l );
135+ int ml = x ;
136+ int mr = x ;
137+ // Process the left side of index x.
138+ if (l != x ) {
139+ groups .put (l , x - 1 );
140+ addGroup (l , x - 1 );
141+ } else {
142+ if (x > 0 && colors [x ] != colors [x - 1 ]) {
143+ it = groups .floorEntry (x - 1 );
144+ if (it != null ) {
145+ ml = it .getKey ();
146+ removeGroup (it .getKey (), it .getValue ());
147+ groups .remove (it .getKey ());
142148 }
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- }
154- }
149+ }
150+ }
151+ // Process the right side of index x.
152+ if (r != x ) {
153+ groups .put (x + 1 , r );
154+ addGroup (x + 1 , r );
155+ } else {
156+ if (x + 1 < colors .length && colors [x + 1 ] != colors [x ]) {
157+ it = groups .ceilingEntry (x + 1 );
158+ if (it != null ) {
159+ mr = it .getValue ();
160+ removeGroup (it .getKey (), it .getValue ());
161+ groups .remove (it .getKey ());
155162 }
156- c .put (ml , mr );
157- // Add new or modified block
158- edt (len (ml , mr ), 1 );
159163 }
160164 }
161- // Clear BITs after processing all queries
165+
166+ // Merge the affected groups into one new group.
167+ groups .put (ml , mr );
168+ addGroup (ml , mr );
169+ }
170+
171+ // Clears both BITs. This is done after processing all queries.
172+ private void clearAllBITs (int n ) {
162173 for (int i = 0 ; i <= n + 2 ; ++i ) {
163174 BITS [0 ].clear (i );
164175 BITS [1 ].clear (i );
165176 }
177+ }
178+
179+ // Main function to handle queries on alternating groups.
180+ public List <Integer > numberOfAlternatingGroups (int [] colors , int [][] queries ) {
181+ TreeMap <Integer , Integer > groups = new TreeMap <>();
182+ int n = colors .length ;
183+ List <Integer > results = new ArrayList <>();
184+ // Initialize alternating groups.
185+ initializeGroups (colors , groups );
186+ // Process each query.
187+ for (int [] query : queries ) {
188+ if (query [0 ] == 1 ) {
189+ // Type 1 query: count alternating groups of at least a given size.
190+ int groupSize = query [1 ];
191+ int ans = processQueryType1 (colors , groups , groupSize );
192+ results .add (ans );
193+ } else {
194+ // Type 2 query: update the color at a given index.
195+ int index = query [1 ];
196+ int newColor = query [2 ];
197+ processQueryType2 (colors , groups , index , newColor );
198+ }
199+ }
200+ // Clear BITs after processing all queries.
201+ clearAllBITs (n );
166202 return results ;
167203 }
168204}
0 commit comments