Introduction
CCPC 2021 Online E - Monopoly asks for the earliest arrival time in a process that repeats forever. With direct simulation, there is no clear bound on the number of laps. Only the positions within a single lap form a finite set; every complete lap changes the score by the same cycle sum.
The Problem
The board contains integers arranged in a circle. The initial score is . The first step lands on , and movement then continues cyclically in order, adding the value at the current position to the score at every step.
Each query gives a target score . Output the number of steps when the score first becomes exactly , or if it cannot be reached. The initial state already has score , so the answer for is .
For one test case, . Across all test cases, the sums of and are each at most . The target score can have absolute value as large as , so enumerating steps or complete cycles is infeasible.
Positions Within a Cycle
Define the prefix sums of the first lap:
The score change over one complete lap is:
In the implementation, pre[i] stores the within-cycle prefix (), while s stores the total of one complete cycle.
Any step count can be written uniquely as:
Here, is the number of completed laps, and is the number of steps already taken in the current lap. The score is:
Keep only through . and at the start of the next lap represent the same cycle boundary. Keeping both would give two representations of the same time.
For a query , we now need to find a within-cycle position satisfying:
while minimizing .
A Zero Cycle Sum
If , a complete lap does not change the score:
Every reachable score already appears in the first lap. For each prefix sum, record only the earliest step where it occurs, then answer queries by looking it up in a hash table. The initial prefix corresponds to step .
Later occurrences of the same prefix sum have larger step counts and cannot improve the answer.
f1st stores this mapping from prefix sum to earliest position. When building it, insert a prefix sum only on its first occurrence, so solve1 naturally retrieves the earliest step:
1 | if (!f1st.count(s)) { |
Grouping by Congruence Class
Now consider , and let:
Complete cycles change the score by integer multiples of . A necessary condition for a within-cycle prefix to produce the target is:
Group the prefixes by their nonnegative remainder modulo . For a fixed remainder , write:
Congruence ensures that the required number of laps is an integer. The remaining constraint is . The sign of the cycle sum determines which side of the sorted array contains the valid prefixes.
In the implementation, mod normalizes negative remainders to , and same_rem stores candidate prefixes by remainder. Each candidate’s first component is ; its second component, val, stores the corresponding step-count adjustment in each of the two cases below.
A Positive Cycle Sum
Here, . Substituting into:
gives:
The validity condition is:
The corresponding number of steps is:
For a fixed query, is constant. Sort the prefixes within each congruence class by in ascending order, and maintain prefix minima of:
For a query, binary-search for the last prefix no greater than and retrieve the minimum through that position.
In the implementation, val = i - k * n stores , and best stores prefix minima after sorting by . upper_bound locates the last position satisfying , and the answer is h * n + best[rem][idx]. If even the smallest prefix in the group is greater than , there is no valid nonnegative number of laps.
A Negative Cycle Sum
Here, , so the equation becomes:
Therefore:
The validity condition is:
The number of steps can be written as:
We still sort each congruence class by in ascending order, but the valid candidates now form a suffix. Maintain suffix minima of:
For a query, use lower_bound to find the first prefix at least , then retrieve the corresponding suffix minimum.
The adjustment here is val = i + k * n; best stores suffix minima, and the final answer is -h * n + best[rem][idx]. The positive and negative cases share the same congruence grouping; only the direction of the valid range and of the maintained minima changes.
If even the largest prefix in the group is less than , the target is likewise unreachable.
Implementation Details and Boundaries
Taking the remainder of a negative number in C++ may produce a negative remainder. When grouping, always use:
to place remainders in , so that negative prefixes and negative queries can enter the same group.
A prefix value may occur more than once; deduplication is unnecessary. The prefix or suffix minima automatically retain the candidate with the earlier step count.
For a query , output directly. Even if the score returns to zero later, it cannot do so earlier than the initial state.
Prefix sums, queries, quotients, and expressions for step counts can all exceed the range of int, so use long long throughout.
Complexity
When , building the hash table of earliest prefix positions takes on average, and each query takes on average.
When , sorting all the groups takes in total, and constructing the minima arrays takes another . Each query first finds its congruence class through a hash table, then binary-searches for the valid range within that group. The average total time complexity is therefore:
The space complexity is .
Accepted Code
Expand/collapse the final accepted code
1 |
|