IOI 2002
IOI 2002

XOR

Problem Statement Summary Given an M N grid of 0s and 1s (M, N 500), find the largest-area subrectangle whose XOR (equivalently, the parity of its number of 1s) equals 1. Solution: 2D Prefix XOR Prefix XOR Define the...

Updated May 21, 2026
Track IOI
Year 2002
Statement Not mirrored
TeXC++

Problem Statement

No standalone statement file is available for this entry.

A separate statement file is not available for this entry, so the page focuses on the editorial and implementation.

Editorial

The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.

Problem Statement Summary

Given an $M \times N$ grid of 0s and 1s ($M, N \le 500$), find the largest-area subrectangle whose XOR (equivalently, the parity of its number of 1s) equals 1.

Solution: 2D Prefix XOR

Prefix XOR

Define the 2D prefix XOR: \[ P[i][j] = \bigoplus_{r=1}^{i}\bigoplus_{c=1}^{j} a[r][c]. \] The XOR of subrectangle $(r_1, c_1)$ to $(r_2, c_2)$ is: \[ P[r_2][c_2] \oplus P[r_1{-}1][c_2] \oplus P[r_2][c_1{-}1] \oplus P[r_1{-}1][c_1{-}1]. \]

Reduction to 1D

Fix rows $r_1$ and $r_2$. Define $Q[c] = P[r_2][c] \oplus P[r_1{-}1][c]$. The subrectangle XOR becomes $Q[c_2] \oplus Q[c_1{-}1]$, which equals 1 if and only if $Q[c_2] \ne Q[c_1{-}1]$.

To maximize $c_2 - c_1 + 1$, for each $c_2$ we want the smallest $c_1{-}1$ such that $Q[c_1{-}1] \ne Q[c_2]$. Since $Q$ values are in $\{0, 1\}$, it suffices to track the first occurrence of each value:

Lemma.

Let $\mathrm{first}[b]$ be the smallest index $c'$ with $Q[c'] = b$ (scanning $c' = 0, 1, \ldots$). Then for a given $c_2$, the widest subrectangle ending at column $c_2$ has width $c_2 - \mathrm{first}[1 - Q[c_2]]$ (provided $\mathrm{first}[1 - Q[c_2]]$ exists; otherwise no valid subrectangle ends at $c_2$).

C++ Implementation

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int M, N;
    cin >> M >> N;

    vector<vector<int>> a(M + 1, vector<int>(N + 1, 0));
    for (int i = 1; i <= M; i++)
        for (int j = 1; j <= N; j++)
            cin >> a[i][j];

    // 2D prefix XOR
    vector<vector<int>> P(M + 1, vector<int>(N + 1, 0));
    for (int i = 1; i <= M; i++)
        for (int j = 1; j <= N; j++)
            P[i][j] = a[i][j] ^ P[i - 1][j]
                     ^ P[i][j - 1] ^ P[i - 1][j - 1];

    int bestArea = 0;

    for (int r1 = 1; r1 <= M; r1++) {
        for (int r2 = r1; r2 <= M; r2++) {
            int height = r2 - r1 + 1;

            // Track first occurrence of Q-values 0 and 1.
            // Q[0] = P[r2][0] ^ P[r1-1][0] = 0, so first[0] = 0.
            int first[2] = {0, -1};

            for (int c = 1; c <= N; c++) {
                int qc = P[r2][c] ^ P[r1 - 1][c];
                if (first[qc] == -1)
                    first[qc] = c;

                int need = 1 - qc;
                if (first[need] != -1) {
                    int width = c - first[need];
                    bestArea = max(bestArea, height * width);
                }
            }
        }
    }

    cout << bestArea << "\n";

    return 0;
}

Complexity Analysis

  • Time: $O(M^2 \cdot N)$. We enumerate $O(M^2)$ row pairs and scan $N$ columns per pair.

  • Space: $O(M \cdot N)$ for the prefix XOR array.

Code

C++ solution used for this page.

C++

Clean code view with a raw-file link when you want the original source.

Raw file
// IOI 2002 - XOR
// Find the largest rectangle in an M x N binary grid with XOR = 1.
// Uses 2D prefix XOR and iterates over all row pairs.
// For fixed rows r1, r2: Q[c] = P[r2][c] ^ P[r1-1][c].
// Need Q[c2] ^ Q[c1-1] = 1, i.e., Q[c2] != Q[c1-1].
// Track first occurrence of 0 and 1 in Q to maximize width.
// Complexity: O(M^2 * N) time, O(M * N) space.

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

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int M, N;
    cin >> M >> N;

    vector<vector<int>> a(M + 1, vector<int>(N + 1, 0));
    for (int i = 1; i <= M; i++)
        for (int j = 1; j <= N; j++)
            cin >> a[i][j];

    // 2D prefix XOR
    vector<vector<int>> P(M + 1, vector<int>(N + 1, 0));
    for (int i = 1; i <= M; i++)
        for (int j = 1; j <= N; j++)
            P[i][j] = a[i][j] ^ P[i - 1][j] ^ P[i][j - 1] ^ P[i - 1][j - 1];

    int bestArea = 0;

    // Enumerate all pairs of rows
    for (int r1 = 1; r1 <= M; r1++) {
        for (int r2 = r1; r2 <= M; r2++) {
            int height = r2 - r1 + 1;

            // Q[c] = P[r2][c] ^ P[r1-1][c]
            // Need Q[c2] != Q[c1-1] to get XOR=1, maximize c2 - (c1-1)
            int first[2] = {-1, -1};
            first[0] = 0; // Q[0] = 0 always

            for (int c = 1; c <= N; c++) {
                int qc = P[r2][c] ^ P[r1 - 1][c];
                if (first[qc] == -1) first[qc] = c;

                // Want Q[c1-1] != qc, with c1-1 as small as possible
                int need = 1 - qc;
                if (first[need] != -1) {
                    int width = c - first[need];
                    bestArea = max(bestArea, height * width);
                }
            }
        }
    }

    cout << bestArea << "\n";

    return 0;
}

Source Files and Assets

Raw files are still available here when you want the original TeX, C++, or statement assets.

Show raw files