All IOI entries
Competitive Programming

IOI 2016 - Messy

Given n (a power of 2), determine an unknown permutation P of \ 0, 1,, n-1\ in two phases: Phase 1 (Add): Insert binary strings of length n into a set S. Shuffle: P is applied to every string in S (bit i moves to posi...

Source sync Apr 19, 2026
Track IOI
Year 2016
Files TeX and C++
Folder competitive_programming/ioi/2016/messy
IOI2016TeXC++

Source-first archive entry

This page is built from the copied files in competitive_programming/ioi/2016/messy. Edit competitive_programming/ioi/2016/messy/solution.tex to update the written solution and competitive_programming/ioi/2016/messy/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 $n$ (a power of 2), determine an unknown permutation $P$ of $\{0, 1, \ldots, n-1\}$ in two phases:

  • Phase 1 (Add): Insert binary strings of length $n$ into a set $S$.

  • Shuffle: $P$ is applied to every string in $S$ (bit $i$ moves to position $P(i)$).

  • Phase 2 (Check): Query whether a binary string belongs to the shuffled set.

  • Both phases allow at most $O(n \log n)$ operations.

Editorial

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

Solution

Use divide and conquer on bit positions.

Phase 1: Adding Strings

For D&C on range $[l, r)$ with a ``context'' set $C$ (bit positions outside $[l, r)$ that are always set to 1):

  1. Let $\text{mid} = (l + r) / 2$.

  2. For each $i \in [l, \text{mid})$: add a string with bit $i$ set to 1 and all bits in $C$ set to 1 (all others 0).

  3. Recurse on $[l, \text{mid})$ with $C' = C \cup [\text{mid}, r)$, and on $[\text{mid}, r)$ with $C' = C \cup [l, \text{mid})$.

  4. Each level adds $n/2$ strings across all recursive calls, totalling $O(n \log n)$ strings over $\log n$ levels.

Phase 2: Checking Membership

After the shuffle, for D&C on range $[l, r)$ with a known set of candidate output positions and shuffled context bits:

  1. For each candidate position $p$, check the string with bit $p$ and context bits set. If present, $p$ maps to the left half $[l, \text{mid})$; otherwise, to the right half.

  2. Recurse, using the classified positions as context for sub-problems.

  3. Each level performs $n$ checks, totalling $O(n \log n)$.

C++ Implementation

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

// Grader: void add_element(string), bool check_element(string),
//         void compile_set(), void answer(int[])

int n;
int P[1024];

void addStrings(int l, int r, vector<int> &ctx) {
    if (r - l <= 1) return;
    int mid = (l + r) / 2;
    for (int i = l; i < mid; i++) {
        string s(n, '0');
        s[i] = '1';
        for (int c : ctx) s[c] = '1';
        add_element(s);
    }
    vector<int> leftCtx = ctx;
    for (int i = mid; i < r; i++) leftCtx.push_back(i);
    addStrings(l, mid, leftCtx);

    vector<int> rightCtx = ctx;
    for (int i = l; i < mid; i++) rightCtx.push_back(i);
    addStrings(mid, r, rightCtx);
}

void findPerm(int l, int r, vector<int> &positions, vector<int> &ctx) {
    if (r - l == 1) { P[l] = positions[0]; return; }
    int mid = (l + r) / 2;
    vector<int> leftPos, rightPos;
    for (int p : positions) {
        string s(n, '0');
        s[p] = '1';
        for (int c : ctx) s[c] = '1';
        if (check_element(s)) leftPos.push_back(p);
        else rightPos.push_back(p);
    }
    vector<int> leftCtx = ctx;
    for (int p : rightPos) leftCtx.push_back(p);
    findPerm(l, mid, leftPos, leftCtx);

    vector<int> rightCtx = ctx;
    for (int p : leftPos) rightCtx.push_back(p);
    findPerm(mid, r, rightPos, rightCtx);
}

void restore_permutation(int N, int w, int r) {
    n = N;
    vector<int> emptyCtx;
    addStrings(0, n, emptyCtx);
    compile_set();
    vector<int> allPos(n);
    iota(allPos.begin(), allPos.end(), 0);
    vector<int> emptyCtx2;
    findPerm(0, n, allPos, emptyCtx2);
    answer(P);
}

Complexity Analysis

  • Add operations: $O(n \log n)$.

  • Check operations: $O(n \log n)$.

  • Time: $O(n^2 \log n)$ total work (each string operation touches $O(n)$ bits).

  • Space: $O(n \log n)$ for the set of strings.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/2016/messy/solution.cpp

Exact copied implementation source.

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

// Grader functions:
void add_element(string x);
bool check_element(string x);
void compile_set();  // called between phase 1 and phase 2
void answer(int P[]);

int n;
int P[1024]; // the permutation we're trying to find

void addStrings(int l, int r, vector<int> &context) {
    if (r - l <= 1) return;
    int mid = (l + r) / 2;

    // For each i in [l, mid), add string with bit i = 1 and context bits = 1
    for (int i = l; i < mid; i++) {
        string s(n, '0');
        s[i] = '1';
        for (int c : context) s[c] = '1';
        add_element(s);
    }

    // Recurse: left half with context += [mid, r)
    vector<int> leftCtx = context;
    for (int i = mid; i < r; i++) leftCtx.push_back(i);
    addStrings(l, mid, leftCtx);

    // Right half with context += [l, mid)
    vector<int> rightCtx = context;
    for (int i = l; i < mid; i++) rightCtx.push_back(i);
    addStrings(mid, r, rightCtx);
}

void findPerm(int l, int r, vector<int> &positions, vector<int> &context) {
    if (r - l == 1) {
        P[l] = positions[0];
        return;
    }
    int mid = (l + r) / 2;

    // Determine which positions in 'positions' correspond to [l, mid)
    vector<int> leftPos, rightPos;
    for (int p : positions) {
        string s(n, '0');
        s[p] = '1';
        for (int c : context) s[c] = '1';
        if (check_element(s)) {
            leftPos.push_back(p);
        } else {
            rightPos.push_back(p);
        }
    }

    // Recurse
    vector<int> leftCtx = context;
    for (int p : rightPos) leftCtx.push_back(p);
    findPerm(l, mid, leftPos, leftCtx);

    vector<int> rightCtx = context;
    for (int p : leftPos) rightCtx.push_back(p);
    findPerm(mid, r, rightPos, rightCtx);
}

void restore_permutation(int N, int w, int r) {
    n = N;

    // Phase 1: add strings
    vector<int> emptyCtx;
    addStrings(0, n, emptyCtx);

    // Shuffle happens here
    compile_set();

    // Phase 2: determine permutation
    vector<int> allPos(n);
    iota(allPos.begin(), allPos.end(), 0);
    vector<int> emptyCtx2;
    findPerm(0, n, allPos, emptyCtx2);

    // P[i] = position where original bit i ends up
    // Actually P[i] gives us: original position i maps to output position P[i]
    // We need to invert: answer[P[i]] = i (or however the grader expects it)
    answer(P);
}

int main() {
    int N, w, r;
    scanf("%d %d %d", &N, &w, &r);
    restore_permutation(N, w, r);
    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/2016/messy/solution.tex

Exact copied write-up source.

Raw file
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[margin=1in]{geometry}
\usepackage{amsmath,amssymb,amsthm}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{hyperref}

\newtheorem{theorem}{Theorem}
\newtheorem{lemma}[theorem]{Lemma}

\lstset{
  language=C++,
  basicstyle=\ttfamily\small,
  keywordstyle=\color{blue}\bfseries,
  commentstyle=\color{gray},
  stringstyle=\color{red},
  numbers=left,
  numberstyle=\tiny\color{gray},
  breaklines=true,
  frame=single,
  tabsize=4,
  showstringspaces=false
}

\title{IOI 2016 -- Messy}
\author{}
\date{}

\begin{document}
\maketitle

\section{Problem Summary}

Given $n$ (a power of 2), determine an unknown permutation $P$ of $\{0, 1, \ldots, n-1\}$ in two phases:
\begin{itemize}
  \item \textbf{Phase 1 (Add)}: Insert binary strings of length $n$ into a set $S$.
  \item \textbf{Shuffle}: $P$ is applied to every string in $S$ (bit $i$ moves to position $P(i)$).
  \item \textbf{Phase 2 (Check)}: Query whether a binary string belongs to the shuffled set.
\end{itemize}
Both phases allow at most $O(n \log n)$ operations.

\section{Solution}

Use \textbf{divide and conquer on bit positions}.

\subsection{Phase 1: Adding Strings}

For D\&C on range $[l, r)$ with a ``context'' set $C$ (bit positions outside $[l, r)$ that are always set to~1):
\begin{enumerate}
  \item Let $\text{mid} = (l + r) / 2$.
  \item For each $i \in [l, \text{mid})$: add a string with bit $i$ set to~1 and all bits in $C$ set to~1 (all others~0).
  \item Recurse on $[l, \text{mid})$ with $C' = C \cup [\text{mid}, r)$, and on $[\text{mid}, r)$ with $C' = C \cup [l, \text{mid})$.
\end{enumerate}

Each level adds $n/2$ strings across all recursive calls, totalling $O(n \log n)$ strings over $\log n$ levels.

\subsection{Phase 2: Checking Membership}

After the shuffle, for D\&C on range $[l, r)$ with a known set of candidate output positions and shuffled context bits:
\begin{enumerate}
  \item For each candidate position $p$, check the string with bit $p$ and context bits set. If present, $p$ maps to the left half $[l, \text{mid})$; otherwise, to the right half.
  \item Recurse, using the classified positions as context for sub-problems.
\end{enumerate}

Each level performs $n$ checks, totalling $O(n \log n)$.

\section{C++ Implementation}

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

// Grader: void add_element(string), bool check_element(string),
//         void compile_set(), void answer(int[])

int n;
int P[1024];

void addStrings(int l, int r, vector<int> &ctx) {
    if (r - l <= 1) return;
    int mid = (l + r) / 2;
    for (int i = l; i < mid; i++) {
        string s(n, '0');
        s[i] = '1';
        for (int c : ctx) s[c] = '1';
        add_element(s);
    }
    vector<int> leftCtx = ctx;
    for (int i = mid; i < r; i++) leftCtx.push_back(i);
    addStrings(l, mid, leftCtx);

    vector<int> rightCtx = ctx;
    for (int i = l; i < mid; i++) rightCtx.push_back(i);
    addStrings(mid, r, rightCtx);
}

void findPerm(int l, int r, vector<int> &positions, vector<int> &ctx) {
    if (r - l == 1) { P[l] = positions[0]; return; }
    int mid = (l + r) / 2;
    vector<int> leftPos, rightPos;
    for (int p : positions) {
        string s(n, '0');
        s[p] = '1';
        for (int c : ctx) s[c] = '1';
        if (check_element(s)) leftPos.push_back(p);
        else rightPos.push_back(p);
    }
    vector<int> leftCtx = ctx;
    for (int p : rightPos) leftCtx.push_back(p);
    findPerm(l, mid, leftPos, leftCtx);

    vector<int> rightCtx = ctx;
    for (int p : leftPos) rightCtx.push_back(p);
    findPerm(mid, r, rightPos, rightCtx);
}

void restore_permutation(int N, int w, int r) {
    n = N;
    vector<int> emptyCtx;
    addStrings(0, n, emptyCtx);
    compile_set();
    vector<int> allPos(n);
    iota(allPos.begin(), allPos.end(), 0);
    vector<int> emptyCtx2;
    findPerm(0, n, allPos, emptyCtx2);
    answer(P);
}
\end{lstlisting}

\section{Complexity Analysis}

\begin{itemize}
  \item \textbf{Add operations}: $O(n \log n)$.
  \item \textbf{Check operations}: $O(n \log n)$.
  \item \textbf{Time}: $O(n^2 \log n)$ total work (each string operation touches $O(n)$ bits).
  \item \textbf{Space}: $O(n \log n)$ for the set of strings.
\end{itemize}

\end{document}