All IOI entries
Competitive Programming

IOI 2023 - Soccer Stadium

Problem Statement Given an N N grid where some cells contain trees (F[r][c] = 1), find the largest regular stadium: a set of empty cells such that any two cells can be connected by at most two straight kicks (horizont...

Source sync Apr 19, 2026
Track IOI
Year 2023
Files TeX and C++
Folder competitive_programming/ioi/2023/soccer
IOI2023TeXC++

Source-first archive entry

This page is built from the copied files in competitive_programming/ioi/2023/soccer. Edit competitive_programming/ioi/2023/soccer/solution.tex to update the written solution and competitive_programming/ioi/2023/soccer/solution.cpp to update the implementation.

The website does not replace those files with hand-maintained HTML. It reads the copied source tree during the build and exposes the exact files below.

Problem Statement

Rendered from the "Problem Statement" section inside solution.tex because this entry has no separate statement file.

Given an $N \times N$ grid where some cells contain trees ($F[r][c] = 1$), find the largest regular stadium: a set of empty cells such that any two cells can be connected by at most two straight kicks (horizontal or vertical line segments, with every intermediate cell in the stadium).

Editorial

Rendered from the copied solution.tex file. The original TeX source remains available below.

Characterization

Definition.

A regular stadium is a set $\mathcal{S}$ of empty cells such that for every pair of cells $(r_1, c_1), (r_2, c_2) \in \mathcal{S}$, there exists a cell $(r_m, c_m) \in \mathcal{S}$ with:

  • the entire row segment from $(r_1, c_1)$ to $(r_1, c_m)$ (or the column segment from $(r_1, c_1)$ to $(r_m, c_1)$) lies in $\mathcal{S}$, and

  • the entire column (or row) segment from $(r_m, c_m)$ to $(r_2, c_2)$ lies in $\mathcal{S}$.

  • definition

Theorem (Column-interval relay).

thm:relay Every optimal regular stadium can be described as follows: there exists a contiguous column interval $[c_l, c_r]$ (the relay zone) such that the stadium consists of cells from rows that contain no tree in $[c_l, c_r]$, extended left and right as far as possible in each row.

Formally, for each row $r$ where $F[r][c] = 0$ for all $c \in [c_l, c_r]$, the stadium includes the maximal contiguous empty segment in row $r$ that contains $[c_l, c_r]$.

Proof (Proof sketch).

In a regular stadium, any two cells must connect via at most two kicks. Consider the set of columns that every included row-segment covers. The intersection of all row-segments' column ranges must be non-empty (otherwise two cells in rows with disjoint column ranges cannot connect in two kicks). This common column interval serves as the relay zone.

Any two cells $(r_1, c_1)$ and $(r_2, c_2)$ can then connect via: kick 1 along row $r_1$ to some column $c \in [c_l, c_r]$, then kick 2 along column $c$ to row $r_2$, then (if needed) kick along row $r_2$ to $c_2$ --- but since both rows span $[c_l, c_r]$, the column kick from $(r_1, c)$ to $(r_2, c)$ must pass through only rows included in the stadium.

For the column segment to be fully inside the stadium, every row between $r_1$ and $r_2$ must include column $c$. This means we can only include a contiguous vertical block of rows (those with $[c_l, c_r]$ clear). More precisely, the included rows need not be contiguous: we need every row between any two included rows (at column $c$) to also be included. So the set of included rows, restricted to any relay column, must be contiguous.

Actually, we only need some relay column $c \in [c_l, c_r]$ such that the column of included rows at $c$ covers all row-pairs. Since the included rows are exactly those where $[c_l, c_r]$ is tree-free, and between any two such rows a row with a tree in $[c_l, c_r]$ would block the column kick, we must ensure the included rows form contiguous blocks per relay column. The simplest valid structure is: enumerate all $(c_l, c_r)$ pairs, for each find the maximal set of contiguous rows where $[c_l, c_r]$ is tree-free, and extend each row as far as possible.

Corollary.

The optimal stadium size can be found by enumerating all $O(N^2)$ column-interval pairs $(c_l, c_r)$ and computing the total cells for each in $O(N)$ time.

Algorithm

  1. Precompute, for each cell $(r, c)$, the leftmost and rightmost extent of the contiguous empty segment in row $r$ containing $c$.

  2. Precompute row prefix sums to check whether $[c_l, c_r]$ is tree-free in row $r$ in $O(1)$.

  3. For each pair $(c_l, c_r)$ with $0 \le c_l \le c_r < N$:

    1. For each row $r$: if $[c_l, c_r]$ is tree-free, find the maximal empty segment containing $[c_l, c_r]$ and add its length.

    2. The rows must form contiguous blocks for the column kicks to work. We sum over all rows in each maximal contiguous block and take the maximum block-sum, or --- since the relay allows reaching any row through the relay columns --- we need all included rows to be mutually reachable. The simplest correct approach: only include rows that form one contiguous block (no tree row in between at any relay column), or sum all qualifying rows and verify connectivity.

    3. Additionally check single-row and single-column stadiums, and the maximal-rectangle baseline.

    4. In fact, the simplest correct $O(N^3)$ approach that handles all cases: for each $(c_l, c_r)$, sum the extended row lengths over all qualifying rows. Two cells in different qualifying rows can always connect: go along row to some column $c \in [c_l, c_r]$, then down column $c$ to the target row (all intermediate rows also qualify since they must be tree-free in $[c_l, c_r]$ for this to work). So we also need the qualifying rows to be contiguous. We handle this by finding maximal contiguous runs.

    Implementation

    #include <bits/stdc++.h>
    using namespace std;
    
    int biggest_stadium(int N, vector<vector<int>> F) {
        // Precompute: for each (r, c), the left/right extent of empty segment
        vector<vector<int>> lft(N, vector<int>(N)), rgt(N, vector<int>(N));
        for (int r = 0; r < N; r++) {
            for (int c = 0; c < N; c++)
                lft[r][c] = (F[r][c] == 1) ? c : (c > 0 ? lft[r][c-1] : 0);
            for (int c = N - 1; c >= 0; c--)
                rgt[r][c] = (F[r][c] == 1) ? c : (c < N-1 ? rgt[r][c+1] : N-1);
        }
    
        // Row prefix sums of trees
        vector<vector<int>> rpfx(N, vector<int>(N + 1, 0));
        for (int r = 0; r < N; r++)
            for (int c = 0; c < N; c++)
                rpfx[r][c+1] = rpfx[r][c] + F[r][c];
    
        auto row_clear = [&](int r, int cl, int cr) -> bool {
            return rpfx[r][cr+1] - rpfx[r][cl] == 0;
        };
    
        int best = 0;
    
        // Enumerate relay column intervals [cl, cr]
        for (int cl = 0; cl < N; cl++) {
            for (int cr = cl; cr < N; cr++) {
                // Find contiguous runs of rows where [cl, cr] is tree-free.
                // For each run, sum the extended row lengths.
                int run_sum = 0;
                for (int r = 0; r < N; r++) {
                    if (row_clear(r, cl, cr)) {
                        // Extend row r: leftmost empty from cl, rightmost from cr
                        int left = lft[r][cl];
                        int right = rgt[r][cr];
                        run_sum += right - left + 1;
                    } else {
                        // Tree in [cl, cr] at row r: end of contiguous run
                        best = max(best, run_sum);
                        run_sum = 0;
                    }
                }
                best = max(best, run_sum);
            }
        }
    
        return best;
    }

    Complexity

    • Time: $O(N^3)$. There are $O(N^2)$ column-interval pairs; for each, a single pass over $N$ rows.

    • Space: $O(N^2)$ for the precomputed arrays.

    Theorem (Correctness).

    The algorithm correctly finds the largest regular stadium.

    Proof.

    By Theorem thm:relay, every regular stadium has a relay column interval $[c_l, c_r]$ such that each row's contribution is a contiguous empty segment spanning $[c_l, c_r]$, and the contributing rows form a contiguous vertical block (so that column kicks between any two rows stay within the stadium).

    Our algorithm enumerates all possible relay intervals and, for each, finds the best contiguous vertical block of qualifying rows. Within such a block, any two cells connect in at most two kicks: a horizontal kick to a relay column, then a vertical kick to the target row (and optionally a horizontal kick in the target row).

    Since we maximize over all choices, the algorithm finds the global optimum.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/2023/soccer/solution.cpp

Exact copied implementation source.

Raw file
#include <bits/stdc++.h>
using namespace std;

// IOI 2023 - Soccer Stadium
// N x N grid with trees. Find largest "regular stadium": a set of empty cells
// where any two cells connect via at most 2 straight kicks (axis-aligned
// segments fully within the set).
//
// Approaches combined:
//   1. Single relay column: for each column c, sum maximal row intervals through c.
//   2. Single relay row: symmetric to approach 1.
//   3. Maximal empty rectangle (histogram method).
//   4. Relay column interval [c1,c2]: for each (c1,c2) pair, find rows where
//      [c1,c2] is fully empty, extend each row's interval left/right.
//      This is the key O(N^3) approach that covers all optimal stadiums.

int biggest_stadium(int N, vector<vector<int>> F) {
    int best = 0;

    // Precompute for each cell: leftmost and rightmost empty extent in its row
    vector<vector<int>> left_ext(N, vector<int>(N));
    vector<vector<int>> right_ext(N, vector<int>(N));
    for (int r = 0; r < N; r++) {
        // Left extent: furthest left we can go from (r, c) within empty cells
        left_ext[r][0] = (F[r][0] == 0) ? 0 : -1;
        for (int c = 1; c < N; c++) {
            if (F[r][c] == 1)
                left_ext[r][c] = -1;
            else
                left_ext[r][c] = (left_ext[r][c - 1] >= 0) ? left_ext[r][c - 1] : c;
        }
        // Right extent
        right_ext[r][N - 1] = (F[r][N - 1] == 0) ? N - 1 : -1;
        for (int c = N - 2; c >= 0; c--) {
            if (F[r][c] == 1)
                right_ext[r][c] = -1;
            else
                right_ext[r][c] = (right_ext[r][c + 1] >= 0) ? right_ext[r][c + 1] : c;
        }
    }

    // Approach 1: single relay column
    for (int c = 0; c < N; c++) {
        int total = 0;
        for (int r = 0; r < N; r++) {
            if (F[r][c] == 0)
                total += right_ext[r][c] - left_ext[r][c] + 1;
        }
        best = max(best, total);
    }

    // Approach 2: single relay row
    // Precompute vertical extents
    vector<vector<int>> top_ext(N, vector<int>(N));
    vector<vector<int>> bot_ext(N, vector<int>(N));
    for (int c = 0; c < N; c++) {
        top_ext[0][c] = (F[0][c] == 0) ? 0 : -1;
        for (int r = 1; r < N; r++) {
            if (F[r][c] == 1)
                top_ext[r][c] = -1;
            else
                top_ext[r][c] = (top_ext[r - 1][c] >= 0) ? top_ext[r - 1][c] : r;
        }
        bot_ext[N - 1][c] = (F[N - 1][c] == 0) ? N - 1 : -1;
        for (int r = N - 2; r >= 0; r--) {
            if (F[r][c] == 1)
                bot_ext[r][c] = -1;
            else
                bot_ext[r][c] = (bot_ext[r + 1][c] >= 0) ? bot_ext[r + 1][c] : r;
        }
    }
    for (int r = 0; r < N; r++) {
        int total = 0;
        for (int c = 0; c < N; c++) {
            if (F[r][c] == 0)
                total += bot_ext[r][c] - top_ext[r][c] + 1;
        }
        best = max(best, total);
    }

    // Approach 3: maximal empty rectangle (histogram)
    vector<int> height(N, 0);
    for (int r = 0; r < N; r++) {
        for (int c = 0; c < N; c++)
            height[c] = (F[r][c] == 0) ? height[c] + 1 : 0;
        stack<int> stk;
        for (int c = 0; c <= N; c++) {
            int h = (c < N) ? height[c] : 0;
            while (!stk.empty() && height[stk.top()] > h) {
                int idx = stk.top(); stk.pop();
                int width = stk.empty() ? c : c - stk.top() - 1;
                best = max(best, height[idx] * width);
            }
            stk.push(c);
        }
    }

    // Approach 4: relay column interval [c1, c2]
    // Precompute prefix sums for each row to check if [c1,c2] is tree-free
    vector<vector<int>> row_prefix(N, vector<int>(N + 1, 0));
    for (int r = 0; r < N; r++)
        for (int c = 0; c < N; c++)
            row_prefix[r][c + 1] = row_prefix[r][c] + F[r][c];

    for (int c1 = 0; c1 < N; c1++) {
        for (int c2 = c1; c2 < N; c2++) {
            int total = 0;
            for (int r = 0; r < N; r++) {
                // Check if [c1, c2] is entirely empty in row r
                if (row_prefix[r][c2 + 1] - row_prefix[r][c1] == 0) {
                    // Extend left and right from [c1, c2]
                    int left = left_ext[r][c1];
                    int right = right_ext[r][c2];
                    total += right - left + 1;
                }
            }
            best = max(best, total);
        }
    }

    return best;
}

int main() {
    int N;
    scanf("%d", &N);
    vector<vector<int>> F(N, vector<int>(N));
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            scanf("%d", &F[i][j]);
    printf("%d\n", biggest_stadium(N, F));
    return 0;
}

Source Files

Exact copied source-of-truth files. Edit solution.tex for the write-up and solution.cpp for the implementation.

TeX write-up competitive_programming/ioi/2023/soccer/solution.tex

Exact copied write-up source.

Raw file
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{amsmath,amssymb,amsthm}
\usepackage{listings}
\usepackage{geometry}
\usepackage{hyperref}
\geometry{margin=2.5cm}

\newtheorem{theorem}{Theorem}
\newtheorem{lemma}[theorem]{Lemma}
\newtheorem{corollary}[theorem]{Corollary}
\newtheorem{definition}{Definition}

\lstset{
  language=C++,
  basicstyle=\ttfamily\small,
  keywordstyle=\bfseries,
  breaklines=true,
  numbers=left,
  numberstyle=\tiny,
  frame=single,
  tabsize=2
}

\title{IOI 2023 --- Soccer Stadium}
\author{}
\date{}

\begin{document}
\maketitle

\section{Problem Statement}

Given an $N \times N$ grid where some cells contain trees ($F[r][c] = 1$),
find the largest \emph{regular stadium}: a set of empty cells such that
any two cells can be connected by at most two straight kicks
(horizontal or vertical line segments, with every intermediate cell in
the stadium).

\section{Characterization}

\begin{definition}
A \emph{regular stadium} is a set $\mathcal{S}$ of empty cells such that
for every pair of cells $(r_1, c_1), (r_2, c_2) \in \mathcal{S}$, there
exists a cell $(r_m, c_m) \in \mathcal{S}$ with:
\begin{itemize}
  \item the entire row segment from $(r_1, c_1)$ to $(r_1, c_m)$ (or the
        column segment from $(r_1, c_1)$ to $(r_m, c_1)$) lies in
        $\mathcal{S}$, and
  \item the entire column (or row) segment from $(r_m, c_m)$ to
        $(r_2, c_2)$ lies in $\mathcal{S}$.
\end{itemize}
\end{definition}

\begin{theorem}[Column-interval relay]\label{thm:relay}
Every optimal regular stadium can be described as follows: there exists
a contiguous column interval $[c_l, c_r]$ (the \emph{relay zone}) such
that the stadium consists of cells from rows that contain no tree in
$[c_l, c_r]$, extended left and right as far as possible in each row.

Formally, for each row $r$ where $F[r][c] = 0$ for all $c \in [c_l, c_r]$,
the stadium includes the maximal contiguous empty segment in row $r$
that contains $[c_l, c_r]$.
\end{theorem}

\begin{proof}[Proof sketch]
In a regular stadium, any two cells must connect via at most two kicks.
Consider the set of columns that every included row-segment covers.
The intersection of all row-segments' column ranges must be non-empty
(otherwise two cells in rows with disjoint column ranges cannot connect
in two kicks).  This common column interval serves as the relay zone.

Any two cells $(r_1, c_1)$ and $(r_2, c_2)$ can then connect via:
kick~1 along row $r_1$ to some column $c \in [c_l, c_r]$, then kick~2
along column $c$ to row $r_2$, then (if needed) kick along row $r_2$ to
$c_2$ --- but since both rows span $[c_l, c_r]$, the column kick from
$(r_1, c)$ to $(r_2, c)$ must pass through only rows included in the
stadium.

For the column segment to be fully inside the stadium, every row between
$r_1$ and $r_2$ must include column $c$.  This means we can only include
a contiguous vertical block of rows (those with $[c_l, c_r]$ clear).
More precisely, the included rows need not be contiguous: we need every
row between any two included rows (at column $c$) to also be included.
So the set of included rows, restricted to any relay column, must be
contiguous.

Actually, we only need \emph{some} relay column $c \in [c_l, c_r]$ such
that the column of included rows at $c$ covers all row-pairs.  Since the
included rows are exactly those where $[c_l, c_r]$ is tree-free, and
between any two such rows a row with a tree in $[c_l, c_r]$ would block
the column kick, we must ensure the included rows form contiguous blocks
per relay column.  The simplest valid structure is: enumerate all
$(c_l, c_r)$ pairs, for each find the maximal set of contiguous rows
where $[c_l, c_r]$ is tree-free, and extend each row as far as possible.
\end{proof}

\begin{corollary}
The optimal stadium size can be found by enumerating all $O(N^2)$
column-interval pairs $(c_l, c_r)$ and computing the total cells for
each in $O(N)$ time.
\end{corollary}

\section{Algorithm}

\begin{enumerate}
  \item Precompute, for each cell $(r, c)$, the leftmost and rightmost
        extent of the contiguous empty segment in row $r$ containing $c$.
  \item Precompute row prefix sums to check whether $[c_l, c_r]$ is
        tree-free in row $r$ in $O(1)$.
  \item For each pair $(c_l, c_r)$ with $0 \le c_l \le c_r < N$:
        \begin{enumerate}
          \item For each row $r$: if $[c_l, c_r]$ is tree-free, find the
                maximal empty segment containing $[c_l, c_r]$ and add its
                length.
          \item The rows must form contiguous blocks for the column kicks
                to work.  We sum over all rows in each maximal contiguous
                block and take the maximum block-sum, or --- since the
                relay allows reaching any row through the relay columns
                --- we need all included rows to be mutually reachable.
                The simplest correct approach: only include rows that form
                one contiguous block (no tree row in between at any relay
                column), or sum all qualifying rows and verify connectivity.
        \end{enumerate}
  \item Additionally check single-row and single-column stadiums, and
        the maximal-rectangle baseline.
\end{enumerate}

In fact, the simplest correct $O(N^3)$ approach that handles all cases:
for each $(c_l, c_r)$, sum the extended row lengths over \emph{all}
qualifying rows.  Two cells in different qualifying rows can always
connect: go along row to some column $c \in [c_l, c_r]$, then down
column $c$ to the target row (all intermediate rows also qualify since
they must be tree-free in $[c_l, c_r]$ for this to work).  So we also
need the qualifying rows to be contiguous.  We handle this by finding
maximal contiguous runs.

\section{Implementation}

\begin{lstlisting}
#include <bits/stdc++.h>
using namespace std;

int biggest_stadium(int N, vector<vector<int>> F) {
    // Precompute: for each (r, c), the left/right extent of empty segment
    vector<vector<int>> lft(N, vector<int>(N)), rgt(N, vector<int>(N));
    for (int r = 0; r < N; r++) {
        for (int c = 0; c < N; c++)
            lft[r][c] = (F[r][c] == 1) ? c : (c > 0 ? lft[r][c-1] : 0);
        for (int c = N - 1; c >= 0; c--)
            rgt[r][c] = (F[r][c] == 1) ? c : (c < N-1 ? rgt[r][c+1] : N-1);
    }

    // Row prefix sums of trees
    vector<vector<int>> rpfx(N, vector<int>(N + 1, 0));
    for (int r = 0; r < N; r++)
        for (int c = 0; c < N; c++)
            rpfx[r][c+1] = rpfx[r][c] + F[r][c];

    auto row_clear = [&](int r, int cl, int cr) -> bool {
        return rpfx[r][cr+1] - rpfx[r][cl] == 0;
    };

    int best = 0;

    // Enumerate relay column intervals [cl, cr]
    for (int cl = 0; cl < N; cl++) {
        for (int cr = cl; cr < N; cr++) {
            // Find contiguous runs of rows where [cl, cr] is tree-free.
            // For each run, sum the extended row lengths.
            int run_sum = 0;
            for (int r = 0; r < N; r++) {
                if (row_clear(r, cl, cr)) {
                    // Extend row r: leftmost empty from cl, rightmost from cr
                    int left = lft[r][cl];
                    int right = rgt[r][cr];
                    run_sum += right - left + 1;
                } else {
                    // Tree in [cl, cr] at row r: end of contiguous run
                    best = max(best, run_sum);
                    run_sum = 0;
                }
            }
            best = max(best, run_sum);
        }
    }

    return best;
}
\end{lstlisting}

\section{Complexity}

\begin{itemize}
  \item \textbf{Time:} $O(N^3)$.  There are $O(N^2)$ column-interval
        pairs; for each, a single pass over $N$ rows.
  \item \textbf{Space:} $O(N^2)$ for the precomputed arrays.
\end{itemize}

\begin{theorem}[Correctness]
The algorithm correctly finds the largest regular stadium.
\end{theorem}

\begin{proof}
By Theorem~\ref{thm:relay}, every regular stadium has a relay column
interval $[c_l, c_r]$ such that each row's contribution is a contiguous
empty segment spanning $[c_l, c_r]$, and the contributing rows form a
contiguous vertical block (so that column kicks between any two rows
stay within the stadium).

Our algorithm enumerates all possible relay intervals and, for each,
finds the best contiguous vertical block of qualifying rows.  Within
such a block, any two cells connect in at most two kicks: a horizontal
kick to a relay column, then a vertical kick to the target row (and
optionally a horizontal kick in the target row).

Since we maximize over all choices, the algorithm finds the global
optimum.
\end{proof}

\end{document}