Explanation
Let's define as the number of times the value shows up in the range .
Our answer, at a minimum, will be , if we don't perform an operation. Thus, our goal is to find a subarray that will maximize the additional occurrences of we can add to our answer.
If we want to transform all occurrences of a value into , then the contribution to our answer will be
because any value of will get disturbed by the operation needed to transform into . Now, our goal is to find the optimal tuple and add that to our minimum answer.
Consider all elements , where we transform into , and try to find our best subarray that way. Note that in an optimal subarray , we only consider subarrays where , which makes it so that across all values of , there are only endpoints to consider.
Considering we want to find the maximum answer across all subarrays, this hints towards transforming our problem into something similar to the classic max-subarray problem, which was covered in the prefix sums module.
Let's define to be the sorted array of all occurrences of in our array , and have be the array we run our max-subarray algorithm on, where directly corresponds to . Initially, every element in is set to . Now, to handle occurrences of in a possible subarray, we compress the intervals between each value of . More specifically, we perform the following operation for all valid :
With this transformation, we can apply our max-subarray algorithm of choice to get our answer.
Directly implementing the approach above is enough to pass all test cases, and is the implementation featured in the official editorial. The implementation below uses the same idea as the approach described above, but is modified to be considerably more concise.
Recall Kadane's algorithm for the max-subarray sum. The idea is to sweep from left to right, and for every index find the best subarray ending at said index. The greedy strategy is as follows:
- If we are at index , and the previous max subarray ending at is less than zero, we cut it off and start a new array with the singular element at
- Otherwise, we extend the subarray from by appending the current element to it
Our DP algorithm works in a similar way. Consider sweeping left to right, and having equal the number of occurrences of in our current prefix, while also keeping a counter for the number of times we encounter . Then, for this prefix, the countribution for this prefix would be , if we are currently at index .
However, it's not always optimal to take the entire prefix when transforming certain occurrences of into . More specifically, if , then we should cut off this current prefix and start a new subarray. Thus, our DP state transition is
With this, we can calculate the best contribution as being the maximum value of , as we sweep from left to right.
Implementation
Time Complexity:
C++
#include <bits/stdc++.h>constexpr int MAX = 5e5;int dp[MAX + 1];int main() {int n, c;std::cin >> n >> c;int num_c = 0;
Join the USACO Forum!
Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!