
CP Weekly Challenge
CCC 2022 J5 – Square Pool Solution & Analysis
Use sparse tree coordinates, candidate top-left corners, and careful shrinking to find the largest empty square.
Summary
CCC 2022 J5, Square Pool, asks for the side length of the largest square pool that can fit inside an N x N yard without covering any tree.
The tempting brute force approach is to try every square in the yard. That does not work because N can be as large as 500000. The saving detail is that the number of trees is small. Instead of scanning the whole yard, we only try meaningful top-left corners for the square.
The key idea:
- an optimal square can be slid upward until it touches the top boundary or passes just below a tree
- it can also be slid left until it touches the left boundary or passes just right of a tree
So the top row only needs to be 1 or tree_row + 1, and the left column only needs to be 1 or tree_col + 1.
That leaves at most (T + 1) * (T + 1) candidate top-left positions.
Problem Reference
The official problem is CCC 2022 Stage 1 Junior #5, Square Pool. The DMOJ mirror lists the problem as a 10-point partial problem with N up to 500000 and T up to 100.
Understanding the Problem
We are given:
- a square yard with rows and columns numbered from
1toN Ttree positions- no two trees at the same position
We need to output the largest integer M such that there exists an M x M square fully inside the yard and containing no trees.
For a square with top-left corner (r, c) and side length s, it occupies:
rows r to r + s - 1
columns c to c + s - 1
A tree (tr, tc) blocks that square exactly when:
r <= tr <= r + s - 1
c <= tc <= c + s - 1
Why Full Grid DP Is Not the Right Fit
A common first thought is maximum empty-square dynamic programming, where each cell stores the largest empty square ending at that cell.
That pattern works when the grid is small enough to store. Here, the yard can be 500000 x 500000, which is far too large. We cannot allocate the grid, and we cannot scan every cell.
The input is sparse: only the trees matter. So the solution should be based on tree coordinates, not grid cells.
Key Observation: Move the Square Up and Left
Suppose we already have an optimal square. If its top edge is not at row 1, and there is no tree immediately preventing us from moving it upward, then we can slide it up without making it worse.
Similarly, if its left edge is not at column 1, and no tree prevents a left move, we can slide it left.
After sliding as far as possible:
- the top row is either
1or one row below a tree - the left column is either
1or one column right of a tree
So we only need these candidates:
candidate_rows = {1} plus {tree_row + 1}
candidate_cols = {1} plus {tree_col + 1}
Values larger than N are ignored.
Computing the Best Square From One Top-Left Corner
For a fixed top-left corner (r, c), the yard boundary gives an initial maximum:
s <= N - r + 1
s <= N - c + 1
So:
s = min(N - r + 1, N - c + 1)
Now consider every tree.
If a tree is above the top-left corner or left of it:
tr < r or tc < c
then it can never be inside a square starting at (r, c), so we ignore it.
If the tree is down-right of (r, c), the square reaches that tree once s is large enough to cover both its row and its column:
s > tr - r
s > tc - c
To avoid this tree, at least one of those must remain false. The largest safe side length with respect to this tree is:
max(tr - r, tc - c)
So for this top-left corner, we shrink:
s = min(s, max(tr - r, tc - c))
After checking all trees, s is the largest square possible from (r, c).
Algorithm
- Read
N,T, and all tree positions. - Build candidate top rows from
1andtree_row + 1. - Build candidate left columns from
1andtree_col + 1. - For every candidate
(r, c):
- start with the largest square allowed by the yard boundary
- shrink it using every tree down-right of
(r, c) - update the answer
- Print the best side length found.
Python Solution
# CCC 2022 J5 - Square Pool
# Candidate top-left corners + tree-based shrinking
# Time: O(T^3) in the direct form below
# Space: O(T)
N = int(input())
T = int(input())
trees = []
rows = {1}
cols = {1}
for _ in range(T):
r, c = map(int, input().split())
trees.append((r, c))
if r + 1 <= N:
rows.add(r + 1)
if c + 1 <= N:
cols.add(c + 1)
best = 0
for r in rows:
max_by_row = N - r + 1
if max_by_row <= best:
continue
for c in cols:
s = min(max_by_row, N - c + 1)
if s <= best:
continue
for tr, tc in trees:
if tr >= r and tc >= c:
s = min(s, max(tr - r, tc - c))
if s <= best:
break
best = max(best, s)
print(best)
Walkthrough With Sample 1
Input:
5
1
2 4
There is one tree at row 2, column 4.
Candidate top rows:
1, 3
Candidate left columns:
1, 5
Try top-left (3, 1). The boundary allows a 3 x 3 square from rows 3..5 and columns 1..3. The tree at (2, 4) is above it, so it does not block the square.
The answer is 3.
Common Mistakes
- Treating
Nas small and building anN x Ngrid. - Reading
NandTfrom the same line. In the official input,Nis on the first line andTis on the second line. - Using
tree_rowandtree_colas candidate starts instead oftree_row + 1andtree_col + 1. - Forgetting that a tree only matters for a top-left corner if it is down-right of that corner.
- Shrinking by
min(tr - r, tc - c)instead ofmax(tr - r, tc - c).
Review Questions
- Why can the top row of an optimal square be assumed to be either
1ortree_row + 1? - For a fixed top-left corner, why does a down-right tree limit the side length to
max(tr - r, tc - c)? - Why is this problem better solved using tree coordinates than a full grid?
- What pruning checks in the code help avoid unnecessary work?
Questions or feedback?
If you have questions about this article or suggestions for improvement, send us a private message.