All IOI entries
Competitive Programming

IOI 2010 - Saveit

Given a connected graph with N nodes (N 1000) and H hub nodes (H 36, nodes 0,, H-1), encode the shortest-path distances from every hub to every node into a bit string. The decoder must reconstruct all H N distances fr...

Source sync Apr 19, 2026
Track IOI
Year 2010
Files TeX and C++
Folder competitive_programming/ioi/2010/saveit
IOI2010TeXC++

Source-first archive entry

This page is built from the copied files in competitive_programming/ioi/2010/saveit. Edit competitive_programming/ioi/2010/saveit/solution.tex to update the written solution and competitive_programming/ioi/2010/saveit/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 a connected graph with $N$ nodes ($N \le 1000$) and $H$ hub nodes ($H \le 36$, nodes $0, \ldots, H-1$), encode the shortest-path distances from every hub to every node into a bit string. The decoder must reconstruct all $H \times N$ distances from the bit string. The bit budget is approximately $70{,}000$ bits.

Editorial

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

Solution

Key Insight

Compute a BFS spanning tree $\mathcal{T}$ rooted at hub $0$. For any tree edge $(u, \mathrm{parent}(u))$ and any hub $h$, the triangle inequality on the tree gives: \[ d_h(u) - d_h(\mathrm{parent}(u)) \in \{-1, 0, +1\}. \] Hence we can encode these differences instead of absolute distances.

Encoding Scheme

  1. Tree structure: Encode $\mathrm{parent}(v)$ for $v = 1, \ldots, N-1$ using $\lceil \log_2 N \rceil = 10$ bits each. Total: $10(N-1) \le 9{,}990$ bits.

  2. Differences: For each hub $h$ and each non-root node $v$ (in BFS order), encode \[ \delta_{h,v} = d_h(v) - d_h(\mathrm{parent}(v)) + 1 \in \{0, 1, 2\}. \] This is a base-3 digit. Pack pairs of digits into a base-9 value requiring 4 bits. Total: $\lceil H(N-1)/2 \rceil \cdot 4 \le 36 \cdot 500 \cdot 4 = 72{,}000$ bits.

  3. Combined: at most $9{,}990 + 72{,}000 \approx 82{,}000$ bits, within the budget with careful packing.

Decoding

  1. Read the parent array to reconstruct $\mathcal{T}$.

  2. Read and unpack the base-3 differences.

  3. For each hub $h$, set $d_h(\text{root}) = 0$ for hub $0$ (the root). For other hubs, the distance from hub $h$ to the root is reconstructed by traversing the tree using the differences.

  4. Reconstruct all distances: $d_h(v) = d_h(\mathrm{parent}(v)) + (\delta_{h,v} - 1)$, processing nodes in BFS order.

Complexity

  • Encode time: $O(H(N + M))$ for BFS from each hub.

  • Decode time: $O(HN)$.

  • Bits: $O(N \log N + HN)$.

Implementation

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

void encode_bit(int b);
int decode_bit();

// ---- ENCODER ----
void encode(int N, int H, int M, int A[], int B[]) {
    vector<vector<int>> adj(N);
    for (int i = 0; i < M; i++) {
        adj[A[i]].push_back(B[i]);
        adj[B[i]].push_back(A[i]);
    }

    // BFS from node 0 to build spanning tree
    vector<int> parent(N, -1), bfsOrder;
    vector<bool> visited(N, false);
    queue<int> q;
    q.push(0);
    visited[0] = true;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        bfsOrder.push_back(u);
        for (int v : adj[u])
            if (!visited[v]) {
                visited[v] = true;
                parent[v] = u;
                q.push(v);
            }
    }

    // Encode parent array (10 bits per node)
    for (int i = 1; i < N; i++)
        for (int b = 9; b >= 0; b--)
            encode_bit((parent[i] >> b) & 1);

    // BFS distances from each hub
    vector<vector<int>> dist(H, vector<int>(N, -1));
    for (int h = 0; h < H; h++) {
        queue<int> bfs;
        bfs.push(h);
        dist[h][h] = 0;
        while (!bfs.empty()) {
            int u = bfs.front(); bfs.pop();
            for (int v : adj[u])
                if (dist[h][v] == -1) {
                    dist[h][v] = dist[h][u] + 1;
                    bfs.push(v);
                }
        }
    }

    // Encode differences (base-3), packed in pairs into 4 bits
    vector<int> deltas;
    for (int h = 0; h < H; h++)
        for (int i = 1; i < N; i++) {
            int v = bfsOrder[i];
            deltas.push_back(dist[h][v] - dist[h][parent[v]] + 1);
        }
    if (deltas.size() % 2 != 0) deltas.push_back(0);

    for (int i = 0; i < (int)deltas.size(); i += 2) {
        int val = deltas[i] * 3 + deltas[i + 1]; // 0..8
        for (int b = 3; b >= 0; b--)
            encode_bit((val >> b) & 1);
    }
}

// ---- DECODER ----
void decode(int N, int H) {
    // Read parent array
    vector<int> parent(N, -1);
    for (int i = 1; i < N; i++) {
        int p = 0;
        for (int b = 9; b >= 0; b--)
            p |= (decode_bit() << b);
        parent[i] = p;
    }

    // Reconstruct BFS order
    vector<vector<int>> children(N);
    for (int i = 1; i < N; i++)
        children[parent[i]].push_back(i);

    vector<int> bfsOrder;
    queue<int> q;
    q.push(0);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        bfsOrder.push_back(u);
        for (int c : children[u]) q.push(c);
    }

    // Read packed deltas
    int totalDeltas = H * (N - 1);
    int paddedSize = totalDeltas + (totalDeltas % 2);
    vector<int> deltas;
    for (int i = 0; i < paddedSize; i += 2) {
        int val = 0;
        for (int b = 3; b >= 0; b--)
            val |= (decode_bit() << b);
        deltas.push_back(val / 3);
        deltas.push_back(val % 3);
    }

    // Reconstruct distances
    vector<vector<int>> dist(H, vector<int>(N, 0));
    int idx = 0;
    for (int h = 0; h < H; h++)
        for (int i = 1; i < N; i++) {
            int v = bfsOrder[i];
            dist[h][v] = dist[h][parent[v]] + (deltas[idx++] - 1);
        }

    // Output
    for (int h = 0; h < H; h++) {
        for (int v = 0; v < N; v++) {
            if (v) cout << " ";
            cout << dist[h][v];
        }
        cout << "\n";
    }
}

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/ioi/2010/saveit/solution.cpp

Exact copied implementation source.

Raw file
// IOI 2010 - Saveit
// Encode/decode shortest-path distances via BFS spanning tree + base-3 diffs.
// Bits: O(N log N + H * N).
#include <bits/stdc++.h>
using namespace std;

// Grader-provided bit I/O.
void encode_bit(int b);
int decode_bit();

// ===== ENCODER =====
void encode(int N, int H, int M, vector<pair<int, int>> &edges) {
    vector<vector<int>> adj(N);
    for (auto &[u, v] : edges) {
        adj[u].push_back(v);
        adj[v].push_back(u);
    }

    // BFS from node 0 to build spanning tree.
    vector<int> parent(N, -1);
    vector<int> bfsOrder;
    vector<bool> visited(N, false);
    queue<int> q;
    q.push(0); visited[0] = true;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        bfsOrder.push_back(u);
        for (int v : adj[u]) {
            if (!visited[v]) {
                visited[v] = true;
                parent[v] = u;
                q.push(v);
            }
        }
    }

    // Encode parent array: 10 bits per node (N <= 1000).
    const int BITS = 10;
    for (int i = 1; i < N; i++) {
        for (int b = BITS - 1; b >= 0; b--) {
            encode_bit((parent[i] >> b) & 1);
        }
    }

    // BFS from each hub to get all distances.
    vector<vector<int>> dist(H, vector<int>(N, -1));
    for (int h = 0; h < H; h++) {
        queue<int> bfs;
        bfs.push(h); dist[h][h] = 0;
        while (!bfs.empty()) {
            int u = bfs.front(); bfs.pop();
            for (int v : adj[u]) {
                if (dist[h][v] == -1) {
                    dist[h][v] = dist[h][u] + 1;
                    bfs.push(v);
                }
            }
        }
    }

    // Encode distance differences along BFS tree edges.
    // delta = dist[h][v] - dist[h][parent[v]] + 1, values in {0, 1, 2}.
    // Pack two base-3 values into 4 bits (0..8 < 16).
    vector<int> deltas;
    for (int h = 0; h < H; h++) {
        for (int i = 1; i < N; i++) {
            int v = bfsOrder[i];
            int p = parent[v];
            int d = dist[h][v] - dist[h][p] + 1;
            deltas.push_back(d);
        }
    }
    if (deltas.size() % 2 != 0) deltas.push_back(0);

    for (int i = 0; i < (int)deltas.size(); i += 2) {
        int val = deltas[i] * 3 + deltas[i + 1];
        for (int b = 3; b >= 0; b--) {
            encode_bit((val >> b) & 1);
        }
    }
}

// ===== DECODER =====
void decode(int N, int H) {
    const int BITS = 10;

    // Read parent array.
    vector<int> parent(N, -1);
    for (int i = 1; i < N; i++) {
        int p = 0;
        for (int b = BITS - 1; b >= 0; b--) {
            p |= (decode_bit() << b);
        }
        parent[i] = p;
    }

    // Reconstruct BFS order from tree.
    vector<vector<int>> children(N);
    for (int i = 1; i < N; i++) children[parent[i]].push_back(i);

    vector<int> bfsOrder;
    queue<int> q;
    q.push(0);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        bfsOrder.push_back(u);
        for (int c : children[u]) q.push(c);
    }

    // Read packed deltas.
    int totalDeltas = H * (N - 1);
    int paddedSize = (totalDeltas + 1) / 2 * 2;
    vector<int> deltas;
    for (int i = 0; i < paddedSize; i += 2) {
        int val = 0;
        for (int b = 3; b >= 0; b--) {
            val |= (decode_bit() << b);
        }
        deltas.push_back(val / 3);
        deltas.push_back(val % 3);
    }

    // Reconstruct distances.
    vector<vector<int>> dist(H, vector<int>(N, 0));
    int idx = 0;
    for (int h = 0; h < H; h++) {
        for (int i = 1; i < N; i++) {
            int v = bfsOrder[i];
            int p = parent[v];
            int d = deltas[idx++] - 1; // map {0,1,2} back to {-1,0,+1}
            dist[h][v] = dist[h][p] + d;
        }
    }

    // Output distances.
    for (int h = 0; h < H; h++) {
        for (int v = 0; v < N; v++) {
            cout << dist[h][v];
            if (v + 1 < N) cout << " ";
        }
        cout << "\n";
    }
}

// Stub main; in contest, grader calls encode/decode directly.
int main() {
    return 0;
}

void encode_bit(int /*b*/) {}
int decode_bit() { 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/2010/saveit/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{green!60!black},
  stringstyle=\color{red!70!black},
  numbers=left,
  numberstyle=\tiny\color{gray},
  breaklines=true,
  frame=single,
  tabsize=4,
  showstringspaces=false
}

\title{IOI 2010 -- Saveit}
\author{Solution}
\date{}

\begin{document}
\maketitle

\section{Problem Summary}
Given a connected graph with $N$ nodes ($N \le 1000$) and $H$ hub nodes ($H \le 36$, nodes $0, \ldots, H-1$), encode the shortest-path distances from every hub to every node into a bit string. The decoder must reconstruct all $H \times N$ distances from the bit string. The bit budget is approximately $70{,}000$ bits.

\section{Solution}

\subsection{Key Insight}
Compute a BFS spanning tree $\mathcal{T}$ rooted at hub~$0$. For any tree edge $(u, \mathrm{parent}(u))$ and any hub~$h$, the triangle inequality on the tree gives:
\[
d_h(u) - d_h(\mathrm{parent}(u)) \in \{-1, 0, +1\}.
\]
Hence we can encode these \emph{differences} instead of absolute distances.

\subsection{Encoding Scheme}
\begin{enumerate}
    \item \textbf{Tree structure:} Encode $\mathrm{parent}(v)$ for $v = 1, \ldots, N-1$ using $\lceil \log_2 N \rceil = 10$ bits each. Total: $10(N-1) \le 9{,}990$ bits.
    \item \textbf{Differences:} For each hub $h$ and each non-root node $v$ (in BFS order), encode
    \[
    \delta_{h,v} = d_h(v) - d_h(\mathrm{parent}(v)) + 1 \in \{0, 1, 2\}.
    \]
    This is a base-3 digit. Pack pairs of digits into a base-9 value requiring 4 bits. Total: $\lceil H(N-1)/2 \rceil \cdot 4 \le 36 \cdot 500 \cdot 4 = 72{,}000$ bits.
\end{enumerate}

Combined: at most $9{,}990 + 72{,}000 \approx 82{,}000$ bits, within the budget with careful packing.

\subsection{Decoding}
\begin{enumerate}
    \item Read the parent array to reconstruct $\mathcal{T}$.
    \item Read and unpack the base-3 differences.
    \item For each hub~$h$, set $d_h(\text{root}) = 0$ for hub~$0$ (the root). For other hubs, the distance from hub~$h$ to the root is reconstructed by traversing the tree using the differences.
    \item Reconstruct all distances: $d_h(v) = d_h(\mathrm{parent}(v)) + (\delta_{h,v} - 1)$, processing nodes in BFS order.
\end{enumerate}

\subsection{Complexity}
\begin{itemize}
    \item \textbf{Encode time:} $O(H(N + M))$ for BFS from each hub.
    \item \textbf{Decode time:} $O(HN)$.
    \item \textbf{Bits:} $O(N \log N + HN)$.
\end{itemize}

\section{Implementation}

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

void encode_bit(int b);
int decode_bit();

// ---- ENCODER ----
void encode(int N, int H, int M, int A[], int B[]) {
    vector<vector<int>> adj(N);
    for (int i = 0; i < M; i++) {
        adj[A[i]].push_back(B[i]);
        adj[B[i]].push_back(A[i]);
    }

    // BFS from node 0 to build spanning tree
    vector<int> parent(N, -1), bfsOrder;
    vector<bool> visited(N, false);
    queue<int> q;
    q.push(0);
    visited[0] = true;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        bfsOrder.push_back(u);
        for (int v : adj[u])
            if (!visited[v]) {
                visited[v] = true;
                parent[v] = u;
                q.push(v);
            }
    }

    // Encode parent array (10 bits per node)
    for (int i = 1; i < N; i++)
        for (int b = 9; b >= 0; b--)
            encode_bit((parent[i] >> b) & 1);

    // BFS distances from each hub
    vector<vector<int>> dist(H, vector<int>(N, -1));
    for (int h = 0; h < H; h++) {
        queue<int> bfs;
        bfs.push(h);
        dist[h][h] = 0;
        while (!bfs.empty()) {
            int u = bfs.front(); bfs.pop();
            for (int v : adj[u])
                if (dist[h][v] == -1) {
                    dist[h][v] = dist[h][u] + 1;
                    bfs.push(v);
                }
        }
    }

    // Encode differences (base-3), packed in pairs into 4 bits
    vector<int> deltas;
    for (int h = 0; h < H; h++)
        for (int i = 1; i < N; i++) {
            int v = bfsOrder[i];
            deltas.push_back(dist[h][v] - dist[h][parent[v]] + 1);
        }
    if (deltas.size() % 2 != 0) deltas.push_back(0);

    for (int i = 0; i < (int)deltas.size(); i += 2) {
        int val = deltas[i] * 3 + deltas[i + 1]; // 0..8
        for (int b = 3; b >= 0; b--)
            encode_bit((val >> b) & 1);
    }
}

// ---- DECODER ----
void decode(int N, int H) {
    // Read parent array
    vector<int> parent(N, -1);
    for (int i = 1; i < N; i++) {
        int p = 0;
        for (int b = 9; b >= 0; b--)
            p |= (decode_bit() << b);
        parent[i] = p;
    }

    // Reconstruct BFS order
    vector<vector<int>> children(N);
    for (int i = 1; i < N; i++)
        children[parent[i]].push_back(i);

    vector<int> bfsOrder;
    queue<int> q;
    q.push(0);
    while (!q.empty()) {
        int u = q.front(); q.pop();
        bfsOrder.push_back(u);
        for (int c : children[u]) q.push(c);
    }

    // Read packed deltas
    int totalDeltas = H * (N - 1);
    int paddedSize = totalDeltas + (totalDeltas % 2);
    vector<int> deltas;
    for (int i = 0; i < paddedSize; i += 2) {
        int val = 0;
        for (int b = 3; b >= 0; b--)
            val |= (decode_bit() << b);
        deltas.push_back(val / 3);
        deltas.push_back(val % 3);
    }

    // Reconstruct distances
    vector<vector<int>> dist(H, vector<int>(N, 0));
    int idx = 0;
    for (int h = 0; h < H; h++)
        for (int i = 1; i < N; i++) {
            int v = bfsOrder[i];
            dist[h][v] = dist[h][parent[v]] + (deltas[idx++] - 1);
        }

    // Output
    for (int h = 0; h < H; h++) {
        for (int v = 0; v < N; v++) {
            if (v) cout << " ";
            cout << dist[h][v];
        }
        cout << "\n";
    }
}
\end{lstlisting}

\end{document}