All IOI entries
Competitive Programming

IOI 2024 - Mosaic

Given two arrays X[0..N-1] and Y[0..N-1] (with X[0] = Y[0]), define an N N grid where: Row 0: grid[0][j] = X[j] Column 0: grid[i][0] = Y[i] Otherwise: grid[i][j] = 1 if both grid[i-1][j] = 0 and grid[i][j-1] = 0; else...

Source sync Apr 19, 2026
Track IOI
Year 2024
Files TeX and C++
Folder competitive_programming/ioi/2024/mosaic
IOI2024TeXC++

Source-first archive entry

This page is built from the copied files in competitive_programming/ioi/2024/mosaic. Edit competitive_programming/ioi/2024/mosaic/solution.tex to update the written solution and competitive_programming/ioi/2024/mosaic/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 Summary" section inside solution.tex because this entry has no separate statement file.

Given two arrays $X[0..N-1]$ and $Y[0..N-1]$ (with $X[0] = Y[0]$), define an $N \times N$ grid where:

  • Row 0: $\text{grid}[0][j] = X[j]$

  • Column 0: $\text{grid}[i][0] = Y[i]$

  • Otherwise: $\text{grid}[i][j] = 1$ if both $\text{grid}[i-1][j] = 0$ and $\text{grid}[i][j-1] = 0$; else $\text{grid}[i][j] = 0$.

  • Equivalently, $\text{grid}[i][j] = 1 - (\text{grid}[i-1][j] \text{ OR } \text{grid}[i][j-1]) = (1 - \text{grid}[i-1][j]) \text{ AND } (1 - \text{grid}[i][j-1])$.

    Or: $\text{grid}[i][j] = \text{grid}[i-1][j] \text{ XOR } \text{grid}[i][j-1] \text{ XOR } (\text{grid}[i-1][j] \text{ AND } \text{grid}[i][j-1])$... Actually it's simpler: $\text{grid}[i][j] = 1 \iff \text{grid}[i-1][j] = 0$ and $\text{grid}[i][j-1] = 0$. This is equivalent to NOR.

    After computing the grid, answer $Q$ queries: each query asks for the number of 1-cells in a subrectangle $[T, B] \times [L, R]$.

Editorial

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

Solution Approach

Grid Computation

The recurrence $\text{grid}[i][j] = (1 - \text{grid}[i-1][j]) \cdot (1 - \text{grid}[i][j-1])$ can be shown to be equivalent to XOR in many cases. Specifically:

Claim: $\text{grid}[i][j] = X[j] \text{ XOR } Y[i]$ if... no, this doesn't hold in general.

Actually, observe:

  • If we define $G[i][j] = \text{grid}[i][j]$, then $G[i][j] = 1$ iff both $G[i-1][j] = 0$ and $G[i][j-1] = 0$.

  • Equivalently, $G[i][j] = 0$ iff $G[i-1][j] = 1$ or $G[i][j-1] = 1$.

  • So $G[i][j] = 0$ means there exists a 1 on the ``staircase path'' from $(0,j)$ and $(i,0)$ to $(i,j)$.

  • In fact, $G[i][j] = 1$ iff $G[i-1][j] = 0$ and $G[i][j-1] = 0$, which iff there's no 1 in the top-left ``staircase'' from $(i,j)$. More precisely:

    $G[i][j] = 1$ iff $X[j'] = 0$ for all $j' \le j$ and $Y[i'] = 0$ for all $i' \le i$... No, that's also not right.

    Let me just compute it directly: $G[i][j]$ is 1 iff both neighbors (above and left) are 0. We can compute the entire grid in $O(N^2)$.

2D Prefix Sums for Queries

Precompute a 2D prefix sum array $P$ where $P[i][j] = \sum_{r \le i, c \le j} G[r][c]$. Then each query is answered in $O(1)$.

C++ Solution

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

vector<ll> mosaic(vector<int> X, vector<int> Y,
                   vector<int> T, vector<int> B,
                   vector<int> L, vector<int> R) {
    int N = X.size();
    int Q = T.size();

    // Compute grid
    vector<vector<int>> G(N, vector<int>(N));
    for (int j = 0; j < N; j++) G[0][j] = X[j];
    for (int i = 0; i < N; i++) G[i][0] = Y[i];
    for (int i = 1; i < N; i++)
        for (int j = 1; j < N; j++)
            G[i][j] = (G[i-1][j] == 0 && G[i][j-1] == 0) ? 1 : 0;

    // 2D prefix sums
    vector<vector<ll>> P(N + 1, vector<ll>(N + 1, 0));
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++)
            P[i][j] = G[i-1][j-1] + P[i-1][j] + P[i][j-1] - P[i-1][j-1];

    // Answer queries
    vector<ll> ans(Q);
    for (int q = 0; q < Q; q++) {
        int t = T[q], b = B[q], l = L[q], r = R[q];
        ans[q] = P[b+1][r+1] - P[t][r+1] - P[b+1][l] + P[t][l];
    }

    return ans;
}

int main() {
    int N, Q;
    scanf("%d", &N);
    vector<int> X(N), Y(N);
    for (int i = 0; i < N; i++) scanf("%d", &X[i]);
    for (int i = 0; i < N; i++) scanf("%d", &Y[i]);
    scanf("%d", &Q);
    vector<int> T(Q), B(Q), L(Q), R(Q);
    for (int q = 0; q < Q; q++)
        scanf("%d %d %d %d", &T[q], &B[q], &L[q], &R[q]);
    auto ans = mosaic(X, Y, T, B, L, R);
    for (int q = 0; q < Q; q++)
        printf("%lld\n", ans[q]);
    return 0;
}

Complexity Analysis

  • Time complexity: $O(N^2 + Q)$ -- $O(N^2)$ to compute the grid and prefix sums, $O(1)$ per query.

  • Space complexity: $O(N^2)$ for the grid and prefix sums.

  • Note: For very large $N$ (up to $10^6$ or more), the $O(N^2)$ approach is too slow. In that case, we can observe that the grid values depend on the positions of 1s in $X$ and $Y$. Specifically, $G[i][j] = 1$ iff $X[1..j]$ and $Y[1..i]$ are all 0 and $X[0] = Y[0] = 0$. For the constrained subtask where queries are on single rows or have special structure, row-based formulas with binary search can be used.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/2024/mosaic/solution.cpp

Exact copied implementation source.

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

// IOI 2024 - Mosaic
// N x N grid defined by top row X and left column Y (with X[0] == Y[0]).
// Recurrence: G[i][j] = 1 iff G[i-1][j] == 0 AND G[i][j-1] == 0 (NOR).
// Answer Q rectangle-sum queries using 2D prefix sums.
// Time: O(N^2 + Q).

vector<ll> mosaic(vector<int> X, vector<int> Y,
                  vector<int> T, vector<int> B,
                  vector<int> L, vector<int> R) {
    int N = X.size();
    int Q = T.size();

    // Build the grid
    vector<vector<int>> G(N, vector<int>(N));
    for (int j = 0; j < N; j++) G[0][j] = X[j];
    for (int i = 0; i < N; i++) G[i][0] = Y[i];
    for (int i = 1; i < N; i++)
        for (int j = 1; j < N; j++)
            G[i][j] = (G[i - 1][j] == 0 && G[i][j - 1] == 0) ? 1 : 0;

    // 2D prefix sums (1-indexed)
    vector<vector<ll>> P(N + 1, vector<ll>(N + 1, 0));
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++)
            P[i][j] = G[i - 1][j - 1] + P[i - 1][j] + P[i][j - 1] - P[i - 1][j - 1];

    // Answer queries
    vector<ll> ans(Q);
    for (int q = 0; q < Q; q++) {
        int t = T[q], b = B[q], l = L[q], r = R[q];
        ans[q] = P[b + 1][r + 1] - P[t][r + 1] - P[b + 1][l] + P[t][l];
    }

    return ans;
}

int main() {
    int N, Q;
    scanf("%d", &N);
    vector<int> X(N), Y(N);
    for (int i = 0; i < N; i++) scanf("%d", &X[i]);
    for (int i = 0; i < N; i++) scanf("%d", &Y[i]);
    scanf("%d", &Q);
    vector<int> T(Q), B(Q), L(Q), R(Q);
    for (int q = 0; q < Q; q++)
        scanf("%d %d %d %d", &T[q], &B[q], &L[q], &R[q]);
    auto ans = mosaic(X, Y, T, B, L, R);
    for (int q = 0; q < Q; q++)
        printf("%lld\n", ans[q]);
    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/2024/mosaic/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}

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

\title{IOI 2024 -- Mosaic}
\author{Solution}
\date{}

\begin{document}
\maketitle

\section{Problem Summary}
Given two arrays $X[0..N-1]$ and $Y[0..N-1]$ (with $X[0] = Y[0]$), define an $N \times N$ grid where:
\begin{itemize}
  \item Row 0: $\text{grid}[0][j] = X[j]$
  \item Column 0: $\text{grid}[i][0] = Y[i]$
  \item Otherwise: $\text{grid}[i][j] = 1$ if both $\text{grid}[i-1][j] = 0$ and $\text{grid}[i][j-1] = 0$; else $\text{grid}[i][j] = 0$.
\end{itemize}

Equivalently, $\text{grid}[i][j] = 1 - (\text{grid}[i-1][j] \text{ OR } \text{grid}[i][j-1]) = (1 - \text{grid}[i-1][j]) \text{ AND } (1 - \text{grid}[i][j-1])$.

Or: $\text{grid}[i][j] = \text{grid}[i-1][j] \text{ XOR } \text{grid}[i][j-1] \text{ XOR } (\text{grid}[i-1][j] \text{ AND } \text{grid}[i][j-1])$... Actually it's simpler: $\text{grid}[i][j] = 1 \iff \text{grid}[i-1][j] = 0$ and $\text{grid}[i][j-1] = 0$. This is equivalent to NOR.

After computing the grid, answer $Q$ queries: each query asks for the number of 1-cells in a subrectangle $[T, B] \times [L, R]$.

\section{Solution Approach}

\subsection{Grid Computation}
The recurrence $\text{grid}[i][j] = (1 - \text{grid}[i-1][j]) \cdot (1 - \text{grid}[i][j-1])$ can be shown to be equivalent to XOR in many cases. Specifically:

\textbf{Claim:} $\text{grid}[i][j] = X[j] \text{ XOR } Y[i]$ if... no, this doesn't hold in general.

Actually, observe:
\begin{itemize}
  \item If we define $G[i][j] = \text{grid}[i][j]$, then $G[i][j] = 1$ iff both $G[i-1][j] = 0$ and $G[i][j-1] = 0$.
  \item Equivalently, $G[i][j] = 0$ iff $G[i-1][j] = 1$ or $G[i][j-1] = 1$.
  \item So $G[i][j] = 0$ means there exists a 1 on the ``staircase path'' from $(0,j)$ and $(i,0)$ to $(i,j)$.
\end{itemize}

In fact, $G[i][j] = 1$ iff $G[i-1][j] = 0$ and $G[i][j-1] = 0$, which iff there's no 1 in the top-left ``staircase'' from $(i,j)$. More precisely:

$G[i][j] = 1$ iff $X[j'] = 0$ for all $j' \le j$ and $Y[i'] = 0$ for all $i' \le i$... No, that's also not right.

Let me just compute it directly: $G[i][j]$ is 1 iff both neighbors (above and left) are 0. We can compute the entire grid in $O(N^2)$.

\subsection{2D Prefix Sums for Queries}
Precompute a 2D prefix sum array $P$ where $P[i][j] = \sum_{r \le i, c \le j} G[r][c]$. Then each query is answered in $O(1)$.

\section{C++ Solution}

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

vector<ll> mosaic(vector<int> X, vector<int> Y,
                   vector<int> T, vector<int> B,
                   vector<int> L, vector<int> R) {
    int N = X.size();
    int Q = T.size();

    // Compute grid
    vector<vector<int>> G(N, vector<int>(N));
    for (int j = 0; j < N; j++) G[0][j] = X[j];
    for (int i = 0; i < N; i++) G[i][0] = Y[i];
    for (int i = 1; i < N; i++)
        for (int j = 1; j < N; j++)
            G[i][j] = (G[i-1][j] == 0 && G[i][j-1] == 0) ? 1 : 0;

    // 2D prefix sums
    vector<vector<ll>> P(N + 1, vector<ll>(N + 1, 0));
    for (int i = 1; i <= N; i++)
        for (int j = 1; j <= N; j++)
            P[i][j] = G[i-1][j-1] + P[i-1][j] + P[i][j-1] - P[i-1][j-1];

    // Answer queries
    vector<ll> ans(Q);
    for (int q = 0; q < Q; q++) {
        int t = T[q], b = B[q], l = L[q], r = R[q];
        ans[q] = P[b+1][r+1] - P[t][r+1] - P[b+1][l] + P[t][l];
    }

    return ans;
}

int main() {
    int N, Q;
    scanf("%d", &N);
    vector<int> X(N), Y(N);
    for (int i = 0; i < N; i++) scanf("%d", &X[i]);
    for (int i = 0; i < N; i++) scanf("%d", &Y[i]);
    scanf("%d", &Q);
    vector<int> T(Q), B(Q), L(Q), R(Q);
    for (int q = 0; q < Q; q++)
        scanf("%d %d %d %d", &T[q], &B[q], &L[q], &R[q]);
    auto ans = mosaic(X, Y, T, B, L, R);
    for (int q = 0; q < Q; q++)
        printf("%lld\n", ans[q]);
    return 0;
}
\end{lstlisting}

\section{Complexity Analysis}
\begin{itemize}
  \item \textbf{Time complexity:} $O(N^2 + Q)$ -- $O(N^2)$ to compute the grid and prefix sums, $O(1)$ per query.
  \item \textbf{Space complexity:} $O(N^2)$ for the grid and prefix sums.
\end{itemize}

\textbf{Note:} For very large $N$ (up to $10^6$ or more), the $O(N^2)$ approach is too slow. In that case, we can observe that the grid values depend on the positions of 1s in $X$ and $Y$. Specifically, $G[i][j] = 1$ iff $X[1..j]$ and $Y[1..i]$ are all 0 and $X[0] = Y[0] = 0$. For the constrained subtask where queries are on single rows or have special structure, row-based formulas with binary search can be used.

\end{document}