December 3, 2021
Cells in an -by- grid are colored in monochrome:
Find the smallest non-negative integer , such that there exists no square of cells in , with side length , whose perimeter consists entirely of black cells.
Codeforces/Polygon practice problem.
The problem reduces to finding the side length of the largest square whose perimeter is entirely black, or if no black cells exist. We denote such squares valid. Let store the largest length of consecutive black cells, starting from cell and moving left. For white cell , let . Similarly, define for moving upwards. The indices for may be computed in each with the recurrence
and similarly for .
Each square in may be specified by a tuple of upper-left corner and side length . The square is valid if and only if all four edges are entirely black; that is, all four conditions below are satisfied:
For each of the squares, this check for validity costs .
First, compute , similarly to , but for lengths of black cells moving right, and downwards, respectively.
Each of the upper-left-to-bottom-right diagonals may be specified by as the diagonal which passes through cell . Denote the diagonals by . Note that specifies the same diagonal as .
For a given upper-left corner , we check all squares for validity by querying a DSU for the maximum valid for that . To begin, define the reach over the diagonal:
and reverse reach :
Intuitively, the reach specifies the maximum side length for a valid square with bottom-right corner , while the reverse reach specifies similarly for the upper-left corner . In other words, square is valid if and only if both conditions below are satisfied:
We can remove condition ’s dependence on by defining the discounted reach :
may now be rewritten:
We process the upper-left corners along diagonal in increasing order of , decreases. Thus, the set of indices which satisfy never shrinks. More specifically, at , all bottom-right corners with must be added to . As we have already processed at this point, only the bottom-right corners which satisfy need to be added. We may query for these in by pre-sorting in , with radix sort, all the on the diagonal.
At a given upper-left corner , then, the maximal valid bottom-right corner is the maximal which also satisfies :
Formally, we wish to make two types of queries to set :
Observe that for some , if , then . This leads us to consider implementing with a DSU. Should we process upper-left corners in decreasing order of instead of increasing order, increases, and never grows. Thus, instead of , we wish to support .
Before moving on to the reduction to DSU, we first discuss an example.
Take, for example, the following input with from the square with upper-left corner at :
110111 011110 010011 111011 111111 111111
Consider the processing of the major diagonal . As mentioned, we step through the diagonal in decreasing order of .
Observe, as noted previously, that intuitively calculates the maximum row index which may serve as a bottom-right corner for a given upper-left corner . In addition, for any upper-left corner , it may form a valid square with bottom-right corner as long as and . These together describe conditions and , respectively.
To visualize the operations on , we arrange it onto a line with corner on the left and on the right. Initially, all corners are part of :
|||||| 012345
At any point in time, we either remove an element from , or we stand at some point on the line, and look left to identify the index of the first
|
blocking our view. Since is unchanged prior to visiting , we take that as our first example. As is , we must remove all from where is smaller than . Thus, we remove from :|||_|| 012345
Intuitively, has been removed from consideration as a bottom-right corner. This is because any bottom or right edges which extend from it can no longer reach any upper-left corners we have yet to process, or the upper-left corner we are currently processing: .
We wrap up processing upper-left corner by querying for the maximal bottom-right corner with , which is the bottom-right corner . To visualize this, imagine standing at index on the line and looking left for the first
|
blocking your vision (index counts). This is the at , which represents bottom-right corner .|||_|| 012345 ^
This means that the maximal valid square with upper-left corner at has bottom-right corner at , which is not a valid square at all. This is expected, since there is no valid square with upper-left corner at , as the cell is colored white.
In the next step, we process upper-left corner . Noting first that its is , we must remove all indices from which have . Once again, that bottom-right corner is :
||__|| 012345 ^
and when we eventually query, we will stand at index and look left, to which we see the bottom-right corner . This once again means that no valid square exists with upper-left corner .
In the next step, we process upper-left corner . We have its , we we will remove from the indices where now. This turns out to be bottom-right corner :
||__|_ 012345
Now, we stand at index and look left:
||__|_ 012345 ^
Luckily, we see the bottom-right corner , which means that, as expected, the maximal valid square with upper-left corner at has side length . We must now update our answer if necessary.
In the final step, we process upper-left corner . , so we remove any indices where . The only such index in is :
|___|_ 012345
Standing at index and looking left, we see , which means that the maximal valid square starting at has side length .
We reduce into a DSU by implementing it together with array , both over indices on . Intuitively, stores which $Get$s return the same answer, and stores that answer. Initialize each of as disjoint, and each .
: Now, must give the same answer as , so update with . Then update :
: Return for the on the diagonal.
For each of the diagonals, contains elements and is queried times. These queries are bottlednecked by the DSU implementation, costing per diagonal. For the implementation, care must be taken with for the smallest in a diagonal.