D - フェンスの塗り替え / Repainting the Fence Editorial by admin
Claude 4.6 Opus (Thinking)Overview
Given \(N\) boards, we perform \(M\) interval repainting operations in order and determine the final color of each board. Since later paint overwrites earlier paint, each board’s color is determined by “the last operation that painted it.”
Analysis
Problem with the Naive Approach
The simplest method is to paint boards one by one from \(L_i\) to \(R_i\) for each operation. However, a single operation can paint up to \(N = 5 \times 10^5\) boards, and there are \(M = 2 \times 10^5\) operations, resulting in a worst-case complexity of \(O(NM) = 10^{11}\), which causes TLE (Time Limit Exceeded).
Key Insight: Only “the last painted color” matters
The final color of each board is determined by the color of the last operation that painted it. In other words, even if the same board is painted multiple times, all operations except the last one are wasted.
Using this property, we can process operations in reverse order (from back to front). When processing an operation \([L_i, R_i, C_i]\) in reverse order, we only need to assign color \(C_i\) to boards whose color has not yet been determined (i.e., boards not yet assigned to any operation). Once a board’s color is determined, it can be ignored for all subsequent operations (which are earlier operations in the original order).
Problem: How to efficiently find unpainted boards?
In the reverse-order processing, we want to find unpainted boards within the interval \([L_i, R_i]\), but checking one by one could still result in \(O(NM)\). Here we use the path compression technique of Union-Find (disjoint set data structure).
Algorithm
Union-Find for Managing “Next Unpainted Position”
We prepare an array parent[i] that manages “the nearest unpainted board number at position \(i\) or later.”
- Initial state:
parent[i] = i(all boards are unpainted, so each position points to itself) - When board \(i\) is painted: Set
parent[i] = i + 1(position \(i\) is now painted, so the next unpainted position is \(i+1\) or later) find(x): Followsparentwith path compression and returns the first unpainted position at or after position \(x\)
Processing Flow
- Process operations in reverse order (\(i = M, M-1, \ldots, 1\))
- For operation \([L_i, R_i, C_i]\), get the first unpainted position in the interval with
pos = find(L_i) - While
pos <= R_i, assign color \(C_i\) to boardpos, setparent[pos] = pos + 1, and move to the next - After processing all operations, boards that were not assigned any color remain at their initial value \(0\)
Concrete Example
For \(N = 5, M = 2\), operation 1: \([1, 4, 3]\), operation 2: \([2, 5, 7]\):
- Process operation 2 in reverse: Assign color 7 to boards 2, 3, 4, 5 →
boards = [0, 7, 7, 7, 7] - Process operation 1 in reverse: Search for unpainted boards in \([1,4]\) → only board 1 is unpainted → assign color 3 to board 1
- Final result:
3 7 7 7 7
Complexity
- Time complexity: \(O(N + M \cdot \alpha(N))\) (\(\alpha\) is the inverse Ackermann function, which is effectively constant. Since each board is painted at most once, the total number of paintings across all operations is \(O(N)\))
- Space complexity: \(O(N + M)\)
Implementation Notes
The
parentarray should have size \(N+2\).parent[N+1]serves as a sentinel, so whenfindreturns \(N+1\), we know we are outside the intervalIn Python, recursive
findrisks stack overflow, so it is implemented using a loop with path compression (path halving) (parent[x] = parent[parent[x]])Input is read all at once using
sys.stdin.buffer.read()for faster I/OSource Code
import sys
def main():
input_data = sys.stdin.buffer.read().split()
idx = 0
N = int(input_data[idx]); idx += 1
M = int(input_data[idx]); idx += 1
boards = [0] * (N + 1)
# Process operations in reverse order; skip already painted positions
# Use a set or interval structure to track unpainted positions
# We'll use a sorted set approach with an interval list
# We maintain a list of "free" intervals (not yet assigned)
# Initially all [1, N] is free
# Process from last operation to first; for each operation [L, R, C],
# find all free positions in [L, R], assign them C, and remove them from free set.
# Use a simple approach with a "next unpainted" array (like DSU/path compression)
# parent[i] points to the next unpainted position >= i
parent = list(range(N + 2)) # parent[i] for i in 0..N+1
def find(x):
while parent[x] != x:
parent[x] = parent[parent[x]]
x = parent[x]
return x
operations = []
for i in range(M):
L = int(input_data[idx]); idx += 1
R = int(input_data[idx]); idx += 1
C = int(input_data[idx]); idx += 1
operations.append((L, R, C))
# Process in reverse
for i in range(M - 1, -1, -1):
L, R, C = operations[i]
pos = find(L)
while pos <= R:
boards[pos] = C
parent[pos] = pos + 1 # mark as painted, point to next
pos = find(pos + 1)
sys.stdout.write(' '.join(map(str, boards[1:N+1])) + '\n')
main()
This editorial was generated by claude4.6opus-thinking.
posted:
last update: