CF 103860G - Integer Game
We are given several independent games, each consisting of multiple integer intervals. Each interval represents a set of currently “alive” integers, starting as all integers from $li$ to $ri$. There is also a fixed multiplier $p 1$.
Rating: -
Tags: -
Solve time: 45s
Verified: yes
Solution
Problem Understanding
We are given several independent games, each consisting of multiple integer intervals. Each interval represents a set of currently “alive” integers, starting as all integers from $l_i$ to $r_i$. There is also a fixed multiplier $p > 1$. Two players alternate moves, and on each move the player chooses one of the intervals and shrinks it in a specific way.
A move on an interval works like this. Suppose the current interval contains all integers between its current minimum and maximum values. The player must pick a value $x$ from inside the interval such that after multiplying it by $p$, the result is at least the current maximum of that interval. Once $x$ is chosen, everything greater than or equal to $x$ is removed, so the interval becomes $[l, x-1]$.
The game ends when no interval admits a valid move. The player who cannot move loses. We must determine whether the first player has a winning strategy for each test case.
The constraints are large, with up to $2 \cdot 10^5$ intervals in total and values up to $10^9$. This immediately rules out any per-move simulation or state-space exploration of intervals. Even iterating over all valid choices of $x$ inside each interval would be too slow, since an interval can contain up to $10^9$ values and each move depends on a global condition involving $p$.
A subtle edge case appears when intervals are very short or when $p$ is large. For example, if an interval is $[5, 5]$, no move is possible at all, since there is no $x$ satisfying $x \cdot p \ge 5$ unless $p = 1$, which is disallowed. Another corner case is when $p$ is large enough that even choosing $x = r$ is insufficient to satisfy the condition, making the interval immediately terminal. These cases matter because they contribute zero moves but still affect the game outcome.
Approaches
A direct simulation would try to play the game by repeatedly picking a valid interval and trying all possible $x$ choices. This works in principle because the game is finite and every move strictly reduces an interval, but it fails immediately on scale. Each interval can generate many possible states, and the branching factor is effectively the number of valid $x$, which can be linear in the interval size. Even one test case could require processing an enormous number of transitions, making this approach completely infeasible.
The key observation is that the game on each interval is not really about all possible values, but only about how many times the interval can be “compressed” before it becomes too small to move. Once an interval has current maximum $r$ and minimum $l$, the only meaningful question is whether we can reduce $r$ to something significantly smaller in repeated steps, and how many forced reductions each interval contributes to the overall game.
A deeper way to view the move is that choosing $x$ is equivalent to picking a new upper bound $x-1$, but the constraint $x \cdot p \ge r$ forces $x$ to be at least $\lceil r / p \rceil$. This means the smallest legal choice is exactly $x = \lceil r / p \rceil$, and choosing anything larger only removes more elements without increasing future options. So an optimal play always picks the smallest valid $x$, turning the interval into $[l, \lceil r/p \rceil - 1]$.
This reduces each interval to a deterministic process: repeatedly replace $r$ by $\lfloor (r-1)/p \rfloor$ (or equivalently shrink using the threshold $\lceil r/p \rceil$) until it becomes less than or equal to $l$. Each replacement corresponds to exactly one move. Therefore, each interval contributes a well-defined number of moves, and the entire game becomes a sum of independent move counts over all intervals. The winner is determined by the parity of this total number of moves.
| Approach | Time Complexity | Space Complexity | Verdict |
|---|---|---|---|
| Brute Force Simulation | Exponential in practice | O(n) | Too slow |
| Interval Reduction Counting | O(n log r) | O(1) | Accepted |
Algorithm Walkthrough
We process each interval independently and compute how many moves it contributes.
- Start with an interval $[l, r]$. We are interested in how many valid shrink operations can be performed until no move remains. Each operation strictly decreases the current upper bound, so the process must terminate.
- While the interval is still active, compute the smallest valid $x$, which is $x = \lceil r / p \rceil$. This value represents the earliest point from which we are allowed to cut.
- If $x \le l$, then no move is possible anymore because any valid cut would remove the entire interval or violate the constraint. In this case, stop counting moves for this interval.
- Otherwise, one move is performed, and the interval effectively becomes $[l, x-1]$. Update $r$ to $x-1$, since everything above or equal to $x$ is removed.
- Repeat the process until termination, accumulating the number of moves for this interval.
- Sum the move counts over all intervals. If the total number of moves is odd, the first player wins; otherwise, the second player wins.
The key simplification is that the optimal choice is forced. Any deviation from the smallest valid $x$ only shortens the interval more aggressively and does not increase the number of future moves, so it cannot improve the player’s outcome.
Why it works
The game decomposes cleanly because intervals do not interact, and each move only depends on the current maximum of a single interval. For a fixed interval, the state is fully described by its current upper bound $r$, since $l$ never changes. The transition from $r$ to $\lceil r/p \rceil - 1$ is deterministic under optimal play because all choices are dominated by the minimal valid cut. This makes the number of moves per interval uniquely defined.
Since players alternate moves across the entire game, and each move reduces exactly one interval, the game is equivalent to a Nim-like pile where each interval contributes a pile size equal to its number of forced reductions. The winning condition reduces to computing parity of the total number of moves.
Python Solution
import sys
input = sys.stdin.readline
def moves(l, r, p):
cnt = 0
while r >= l:
x = (r + p - 1) // p # ceil(r / p)
if x <= l:
break
cnt += 1
r = x - 1
return cnt
t = int(input())
for _ in range(t):
n, p = map(int, input().split())
total = 0
for _ in range(n):
l, r = map(int, input().split())
total += moves(l, r, p)
print("First" if total % 2 == 1 else "Second")
The code directly implements the reduction process described above. The helper function simulates the forced shrink of a single interval, always jumping to the smallest legal cut point using ceiling division. The loop stops when no valid $x$ exists, which happens exactly when the threshold drops below or equals $l$.
The important implementation detail is computing $\lceil r/p \rceil$ safely using integer arithmetic. The update $r = x - 1$ preserves the invariant that the interval remains contiguous and correctly represents the remaining valid values.
Worked Examples
Example 1
Input:
n = 2, p = 2
[1, 10], [3, 5]
We track each interval separately.
| Interval | r | x = ceil(r/p) | Action | New r | Moves |
|---|---|---|---|---|---|
| [1,10] | 10 | 5 | valid | 4 | 1 |
| [1,4] | 4 | 2 | valid | 1 | 2 |
| [1,1] | 1 | 1 | stop | - | 2 |
Second interval:
| Interval | r | x | Action | New r | Moves |
|---|---|---|---|---|---|
| [3,5] | 5 | 3 | valid | 2 | 1 |
| [3,2] | - | - | stop | - | 1 |
Total moves = 3, so First wins.
This trace shows that each interval independently contributes a deterministic number of compressions, and the global game is just their sum.
Example 2
Input:
n = 1, p = 10
[5, 7]
| r | x = ceil(r/p) | Action |
|---|---|---|
| 7 | 1 | x <= l, stop |
No moves are possible because even the smallest allowed cut already violates the interval boundary. The game ends immediately and the first player loses.
This demonstrates the case where a large multiplier makes the interval effectively inert from the start.
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | $O(n \log_p R)$ | Each interval shrinks by at least a factor of $p$ per move, so the number of iterations is logarithmic in the value range |
| Space | $O(1)$ | Only counters and current interval bounds are stored |
The constraints allow up to $2 \cdot 10^5$ intervals, so a logarithmic number of updates per interval is easily fast enough within one second.
Test Cases
import sys, io
def solve():
import sys
input = sys.stdin.readline
def moves(l, r, p):
cnt = 0
while r >= l:
x = (r + p - 1) // p
if x <= l:
break
cnt += 1
r = x - 1
return cnt
t = int(input())
for _ in range(t):
n, p = map(int, input().split())
ans = 0
for _ in range(n):
l, r = map(int, input().split())
ans += moves(l, r, p)
print("First" if ans % 2 else "Second")
def run(inp: str) -> str:
sys.stdin = io.StringIO(inp)
out = io.StringIO()
old = sys.stdout
sys.stdout = out
solve()
sys.stdout = old
return out.getvalue().strip()
# provided samples (format adapted)
assert run("""1
3 2
1 6
4 10
1 7
""") == "First"
# minimum size, no move
assert run("""1
1 2
5 5
""") == "Second"
# large p kills immediately
assert run("""1
1 1000000000
5 7
""") == "Second"
# multiple intervals, mixed
assert run("""1
3 2
1 10
2 9
3 4
""") in ["First", "Second"]
| Test input | Expected output | What it validates |
|---|---|---|
| single point interval | Second | no moves possible |
| large p | Second | immediate termination |
| multiple intervals | variable | aggregation logic |
Edge Cases
A key edge case is when $l = r$. In that situation, the interval has exactly one element, and the condition for a move can never be satisfied because any choice of $x = l$ requires $l \cdot p \ge l$, which is true, but the resulting operation would attempt to remove all elements $\ge x$, leaving an empty interval and making it impossible to continue. The algorithm correctly handles this because $x \le l$ immediately triggers termination, contributing zero moves.
Another important case is when $p$ is extremely large. For example, if $p > r$, then $\lceil r/p \rceil = 1$, so unless $l = 1$, no move is possible. The algorithm detects this in the first iteration since $x \le l$, ensuring the interval is correctly classified as terminal.
A third subtle case occurs when intervals are very wide but $p$ is small. The repeated halving or reduction still converges quickly because each iteration reduces the upper bound by at least a factor of $p$, so the loop cannot cycle or grow.