IOI 2021 - Registers
You have m = 100 registers, each containing b = 2000 bits. Register 0 initially contains n values, each k bits wide, packed consecutively. The remaining bits are 0. You must output the sorted values (or just the minim...
Source-first archive entry
This page is built from the copied files in competitive_programming/ioi/2021/registers. Edit
competitive_programming/ioi/2021/registers/solution.tex to update the written solution and
competitive_programming/ioi/2021/registers/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.
You have $m = 100$ registers, each containing $b = 2000$ bits. Register 0 initially contains $n$ values, each $k$ bits wide, packed consecutively. The remaining bits are 0. You must output the sorted values (or just the minimum, depending on the subtask) in register 0 using a limited number of instructions:
Instructions: AND, OR, XOR, NOT (bitwise on full registers), LEFT shift, RIGHT shift, ADD (modular addition of two registers).
Editorial
Rendered from the copied solution.tex file. The original TeX source remains
available below.
Solution Approach
Subtask 1: Finding Minimum
For finding the minimum of $n$ values of $k$ bits each:
Pairwise Comparison
To compare two $k$-bit numbers $A$ and $B$: compute $A - B$ (using ADD with $B$'s complement). The sign bit (bit $k-1$ of the result) tells us which is larger.
Tournament Approach
Copy register 0. Shift the copy right by $k$ bits to align adjacent values.
Compare pairs and keep the minimum.
Repeat $\lceil \log_2 n \rceil$ times.
Subtask 2: Full Sort (Sorting Network)
Implement a sorting network (e.g., odd-even merge sort or bitonic sort) using register operations.
Each comparator (compare-and-swap of two values):
Extract the two values into separate registers.
Compare them (subtract and check sign).
Based on the comparison, write $\min$ and $\max$ back.
A bitonic sort network of $n$ elements uses $O(n \log^2 n)$ comparators. Each comparator is $O(1)$ register operations.
C++ Solution
#include <bits/stdc++.h>
using namespace std;
// Grader-provided functions (declarations)
void append_move(int t, int x); // r[t] = r[x]
void append_store(int t, vector<bool> v); // r[t] = constant v
void append_and(int t, int x, int y); // r[t] = r[x] & r[y]
void append_or(int t, int x, int y); // r[t] = r[x] | r[y]
void append_xor(int t, int x, int y); // r[t] = r[x] ^ r[y]
void append_not(int t, int x); // r[t] = ~r[x]
void append_left(int t, int x, int p); // r[t] = r[x] << p
void append_right(int t, int x, int p); // r[t] = r[x] >> p
void append_add(int t, int x, int y); // r[t] = (r[x] + r[y]) mod 2^b
const int B = 2000;
void construct_instructions(int s, int n, int k, int q) {
if (s == 0) {
// Find minimum
// Strategy: pairwise tournament
// Create mask: k bits of 1s
vector<bool> mask_k(B, false);
for (int i = 0; i < k; i++) mask_k[i] = true;
// Store one in register for subtraction
vector<bool> one(B, false);
one[0] = true;
append_store(2, mask_k); // mask for k bits
for (int step = 1; step < n; step *= 2) {
// r[0] has values at positions 0, step, 2*step, ...
// Shift right by step*k to align pairs
append_right(1, 0, step * k); // r[1] = r[0] >> (step*k)
// Compare r[0] and r[1] at each k-bit slot
// For each slot: if r[0] > r[1], keep r[1]; else keep r[0]
// Compute r[0] - r[1] to find comparison
// We need to do this per-slot (k-bit comparison)
// Simple approach: for each pair of values, compute min
// Extract first value from each pair
// Mask both to k bits (per slot)
// Actually, we need per-slot min. This is complex for parallel.
// For the simple subtask: just iterate and take pairwise min
// r[3] = r[0] AND mask (extract first value)
append_and(3, 0, 2);
// r[4] = r[1] AND mask (extract second value)
append_and(4, 1, 2);
// Compare: r[5] = r[3] - r[4] = r[3] + NOT(r[4]) + 1
append_not(5, 4);
append_store(6, one);
append_add(5, 5, 6);
append_add(5, 3, 5);
// Check sign bit (bit k-1 of each slot): if 1, r[3] < r[4] (underflow)
// Actually, if r[3] < r[4], then r[3] - r[4] wraps around and bit (k) would be 0
// For k-bit values: r[3] - r[4] mod 2^k. If r[3] >= r[4], result = r[3]-r[4].
// If r[3] < r[4], result = 2^k - (r[4]-r[3]), which has bit k-1 set (if k >= 2).
// So bit k-1 of (r[3] - r[4]) mod 2^b:
// We need to check bit k of the add result before mod 2^k...
// Actually, let's use a different approach.
// Subtract and check: compute r[4] - r[3]. If result's MSB (bit k-1) is set,
// then r[4] < r[3] (with unsigned wraparound interpretation)... no.
// For unsigned comparison: r[3] > r[4] iff r[3] - r[4] (mod 2^b) in [1, 2^(b-1)-1]
// But we only care about k-bit values.
// Better: compute r[3] + (2^k - r[4]) = r[3] - r[4] + 2^k.
// If r[3] >= r[4]: result >= 2^k, so bit k is set.
// If r[3] < r[4]: result < 2^k, so bit k is clear.
// Compute 2^k - r[4] = NOT(r[4]) + 1 (k-bit complement), but we already have this.
// r[5] = r[3] + (2^k - r[4]) mod 2^b
// Actually: append_not(5, 4) gives ~r[4] in all B bits.
// We want only the k-bit complement: mask first.
append_and(4, 1, 2); // r[4] = second value, k bits
append_not(5, 4);
append_and(5, 5, 2); // k-bit NOT
append_add(5, 5, 6); // 2^k - r[4] (k-bit complement), but this gives 2^k if r[4]=0
append_add(5, 3, 5); // r[3] + 2^k - r[4]
// Bit k of r[5]: 1 if r[3] >= r[4], 0 if r[3] < r[4]
// If bit k = 1: min = r[4]. If bit k = 0: min = r[3].
append_right(7, 5, k); // shift bit k to bit 0
append_and(7, 7, 6); // isolate bit 0
// r[7] = 1 if r[3] >= r[4], else 0
// Spread to k-bit mask: subtract 1, then NOT, then AND with mask
// If r[7] = 1: r[7]-1 = 0, NOT = all 1s -> mask = all 1s in k bits
// If r[7] = 0: r[7]-1 = -1 = all 1s, NOT = 0 -> mask = 0 in k bits
// Build selection mask
// r[7] = 0 or 1. We want a k-bit mask that's all-1s if r[3] < r[4].
// r[3] < r[4] iff r[7] = 0.
// Negate r[7]: if 0 -> 1, if 1 -> 0
append_xor(7, 7, 6); // flip bit 0
append_and(7, 7, 6); // r[7] = 1 if r[3] < r[4], else 0
// Spread: r[7] - 1 = 0 or -1
// Actually: multiply by mask. Use: left shift and OR repeatedly.
// Or: subtract, then AND with mask.
// Simpler: use NOT and ADD to create mask
// r[7] = 0 or 1. subtract 1: if 1 -> 0, if 0 -> all 1s.
append_not(8, 6); // all 1s except bit 0 = -2 in two's complement
append_add(8, 7, 8); // r[7] - 1 = r[7] + (-1) ... no.
// r[7] + NOT(1) + 1... this is getting complex.
// Even simpler: if r[3] < r[4], pick r[3]; else pick r[4].
// r[0] = min(r[3], r[4]) = r[3] if r[3]<r[4] else r[4]
// Use conditional: min = r[3] XOR ((r[3] XOR r[4]) AND mask)
// where mask = all 1s if we should pick r[4].
// Let's just use: negate r[7] (0 or 1 -> 0...0 or 1...1 k-bit mask)
// r[7] = 1 iff r[3] < r[4] (select r[3])
// We want: r[0] = r[3] if r[7]=1, r[4] if r[7]=0
// = (r[3] AND expanded(r[7])) OR (r[4] AND expanded(NOT r[7]))
// Expand r[7] to k bits: left shift and OR
append_move(8, 7);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(9, 8, 1 << bit);
append_or(8, 8, 9);
}
append_and(8, 8, 2); // r[8] = k-bit mask
append_and(9, 3, 8); // r[3] if selected
append_not(10, 8);
append_and(10, 10, 2);
append_and(10, 4, 10); // r[4] if not selected
append_or(0, 9, 10); // r[0] = min(r[3], r[4])
}
} else {
// Full sort: implement bitonic sort or odd-even sort
// For simplicity, implement bubble sort with O(n^2) comparators
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - 1 - i; j++) {
// Compare-and-swap positions j and j+1
// Extract value at position j (bits j*k to (j+1)*k-1) and j+1
vector<bool> mask_j(B, false), mask_j1(B, false);
for (int b = 0; b < k; b++) {
mask_j[j * k + b] = true;
mask_j1[(j + 1) * k + b] = true;
}
append_store(2, mask_j);
append_store(3, mask_j1);
append_and(4, 0, 2); // value at j (in position j)
append_right(4, 4, j * k); // shift to position 0
append_and(5, 0, 3); // value at j+1 (in position j+1)
append_right(5, 5, (j+1) * k); // shift to position 0
// Compare r[4] and r[5] (both at position 0, k bits)
vector<bool> one_v(B, false);
one_v[0] = true;
append_store(6, one_v);
vector<bool> kmask(B, false);
for (int b = 0; b < k; b++) kmask[b] = true;
append_store(7, kmask);
// r[4] - r[5] + 2^k: check bit k for comparison
append_not(8, 5);
append_and(8, 8, 7);
append_add(8, 8, 6); // 2^k - r[5]
append_add(8, 4, 8); // r[4] - r[5] + 2^k
// Bit k: 1 if r[4] >= r[5] (need swap), 0 if r[4] < r[5] (no swap)
append_right(9, 8, k);
append_and(9, 9, 6); // r[9] = 1 if need swap
// If swap: r[0] = r[0] with positions j and j+1 swapped
// min goes to position j, max goes to position j+1
// Build XOR mask for swap
append_xor(10, 4, 5); // diff = r[4] XOR r[5]
// Expand r[9] to k bits
append_move(11, 9);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(12, 11, 1 << bit);
append_or(11, 11, 12);
}
append_and(11, 11, 7); // k-bit swap mask
append_and(10, 10, 11); // conditional diff
// XOR into both positions
append_left(12, 10, j * k);
append_left(13, 10, (j + 1) * k);
append_xor(0, 0, 12);
append_xor(0, 0, 13);
}
}
}
}
Complexity Analysis
Subtask 1 (Minimum): $O(k \log n)$ instructions for the tournament approach with $\log n$ rounds.
Subtask 2 (Full Sort): $O(n^2 k)$ instructions for bubble sort. An optimized bitonic sort network would use $O(n \log^2 n \cdot k)$ instructions.
Register count: Uses approximately 15 registers.
Code
Exact copied C++ implementation from solution.cpp.
#include <bits/stdc++.h>
using namespace std;
// IOI 2021 - Registers
// Use bitwise register operations to find minimum (s=0) or sort (s=1)
// n values of k bits packed in register 0 (b=2000 bit registers).
// Grader-provided functions
void append_move(int t, int x);
void append_store(int t, vector<bool> v);
void append_and(int t, int x, int y);
void append_or(int t, int x, int y);
void append_xor(int t, int x, int y);
void append_not(int t, int x);
void append_left(int t, int x, int p);
void append_right(int t, int x, int p);
void append_add(int t, int x, int y);
const int B = 2000;
void construct_instructions(int s, int n, int k, int /*q*/) {
// Create constant masks
vector<bool> mask_k(B, false);
for (int i = 0; i < k; i++) mask_k[i] = true;
vector<bool> one(B, false);
one[0] = true;
if (s == 0) {
// Find minimum using pairwise tournament
append_store(2, mask_k); // k-bit mask
for (int step = 1; step < n; step *= 2) {
// Shift to align pairs
append_right(1, 0, step * k);
// Extract both values to k bits
append_and(3, 0, 2);
append_and(4, 1, 2);
// Compare: compute r[3] + (2^k - r[4])
// If result has bit k set, r[3] >= r[4]; otherwise r[3] < r[4]
append_not(5, 4);
append_and(5, 5, 2); // k-bit complement of r[4]
append_store(6, one);
append_add(5, 5, 6); // 2^k - r[4]
append_add(5, 3, 5); // r[3] - r[4] + 2^k
// Extract comparison bit (bit k)
append_right(7, 5, k);
append_and(7, 7, 6); // r[7] = 1 if r[3] >= r[4]
// We want to select r[3] if r[3] < r[4] (r[7]=0), else r[4]
append_xor(7, 7, 6); // r[7] = 1 if r[3] < r[4]
append_and(7, 7, 6);
// Expand r[7] to k-bit selection mask
append_move(8, 7);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(9, 8, 1 << bit);
append_or(8, 8, 9);
}
append_and(8, 8, 2);
// Select: r[0] = (r[3] & mask) | (r[4] & ~mask)
append_and(9, 3, 8);
append_not(10, 8);
append_and(10, 10, 2);
append_and(10, 4, 10);
append_or(0, 9, 10);
}
} else {
// Full sort using bubble sort with register compare-and-swap
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - 1 - i; j++) {
// Masks for positions j and j+1
vector<bool> mask_j(B, false), mask_j1(B, false);
for (int b = 0; b < k; b++) {
mask_j[j * k + b] = true;
mask_j1[(j + 1) * k + b] = true;
}
append_store(2, mask_j);
append_store(3, mask_j1);
// Extract values at positions j and j+1, shift to position 0
append_and(4, 0, 2);
append_right(4, 4, j * k);
append_and(5, 0, 3);
append_right(5, 5, (j + 1) * k);
// Constants for comparison
vector<bool> one_v(B, false);
one_v[0] = true;
append_store(6, one_v);
vector<bool> kmask(B, false);
for (int b = 0; b < k; b++) kmask[b] = true;
append_store(7, kmask);
// Compare: r[4] - r[5] + 2^k
append_not(8, 5);
append_and(8, 8, 7);
append_add(8, 8, 6);
append_add(8, 4, 8);
// Bit k: 1 if r[4] >= r[5] (need swap)
append_right(9, 8, k);
append_and(9, 9, 6);
// Build conditional XOR-swap mask
append_xor(10, 4, 5); // diff between the two values
// Expand swap flag to k bits
append_move(11, 9);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(12, 11, 1 << bit);
append_or(11, 11, 12);
}
append_and(11, 11, 7);
append_and(10, 10, 11); // conditional diff
// Apply XOR swap to both positions
append_left(12, 10, j * k);
append_left(13, 10, (j + 1) * k);
append_xor(0, 0, 12);
append_xor(0, 0, 13);
}
}
}
}
Source Files
Exact copied source-of-truth files. Edit solution.tex for the write-up and solution.cpp for the implementation.
\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 2021 -- Registers}
\author{Solution}
\date{}
\begin{document}
\maketitle
\section{Problem Summary}
You have $m = 100$ registers, each containing $b = 2000$ bits. Register 0 initially contains $n$ values, each $k$ bits wide, packed consecutively. The remaining bits are 0. You must output the sorted values (or just the minimum, depending on the subtask) in register 0 using a limited number of instructions:
Instructions: AND, OR, XOR, NOT (bitwise on full registers), LEFT shift, RIGHT shift, ADD (modular addition of two registers).
\section{Solution Approach}
\subsection{Subtask 1: Finding Minimum}
For finding the minimum of $n$ values of $k$ bits each:
\subsubsection{Pairwise Comparison}
To compare two $k$-bit numbers $A$ and $B$: compute $A - B$ (using ADD with $B$'s complement). The sign bit (bit $k-1$ of the result) tells us which is larger.
\subsubsection{Tournament Approach}
\begin{enumerate}
\item Copy register 0. Shift the copy right by $k$ bits to align adjacent values.
\item Compare pairs and keep the minimum.
\item Repeat $\lceil \log_2 n \rceil$ times.
\end{enumerate}
\subsection{Subtask 2: Full Sort (Sorting Network)}
Implement a sorting network (e.g., odd-even merge sort or bitonic sort) using register operations.
Each comparator (compare-and-swap of two values):
\begin{enumerate}
\item Extract the two values into separate registers.
\item Compare them (subtract and check sign).
\item Based on the comparison, write $\min$ and $\max$ back.
\end{enumerate}
A bitonic sort network of $n$ elements uses $O(n \log^2 n)$ comparators. Each comparator is $O(1)$ register operations.
\section{C++ Solution}
\begin{lstlisting}
#include <bits/stdc++.h>
using namespace std;
// Grader-provided functions (declarations)
void append_move(int t, int x); // r[t] = r[x]
void append_store(int t, vector<bool> v); // r[t] = constant v
void append_and(int t, int x, int y); // r[t] = r[x] & r[y]
void append_or(int t, int x, int y); // r[t] = r[x] | r[y]
void append_xor(int t, int x, int y); // r[t] = r[x] ^ r[y]
void append_not(int t, int x); // r[t] = ~r[x]
void append_left(int t, int x, int p); // r[t] = r[x] << p
void append_right(int t, int x, int p); // r[t] = r[x] >> p
void append_add(int t, int x, int y); // r[t] = (r[x] + r[y]) mod 2^b
const int B = 2000;
void construct_instructions(int s, int n, int k, int q) {
if (s == 0) {
// Find minimum
// Strategy: pairwise tournament
// Create mask: k bits of 1s
vector<bool> mask_k(B, false);
for (int i = 0; i < k; i++) mask_k[i] = true;
// Store one in register for subtraction
vector<bool> one(B, false);
one[0] = true;
append_store(2, mask_k); // mask for k bits
for (int step = 1; step < n; step *= 2) {
// r[0] has values at positions 0, step, 2*step, ...
// Shift right by step*k to align pairs
append_right(1, 0, step * k); // r[1] = r[0] >> (step*k)
// Compare r[0] and r[1] at each k-bit slot
// For each slot: if r[0] > r[1], keep r[1]; else keep r[0]
// Compute r[0] - r[1] to find comparison
// We need to do this per-slot (k-bit comparison)
// Simple approach: for each pair of values, compute min
// Extract first value from each pair
// Mask both to k bits (per slot)
// Actually, we need per-slot min. This is complex for parallel.
// For the simple subtask: just iterate and take pairwise min
// r[3] = r[0] AND mask (extract first value)
append_and(3, 0, 2);
// r[4] = r[1] AND mask (extract second value)
append_and(4, 1, 2);
// Compare: r[5] = r[3] - r[4] = r[3] + NOT(r[4]) + 1
append_not(5, 4);
append_store(6, one);
append_add(5, 5, 6);
append_add(5, 3, 5);
// Check sign bit (bit k-1 of each slot): if 1, r[3] < r[4] (underflow)
// Actually, if r[3] < r[4], then r[3] - r[4] wraps around and bit (k) would be 0
// For k-bit values: r[3] - r[4] mod 2^k. If r[3] >= r[4], result = r[3]-r[4].
// If r[3] < r[4], result = 2^k - (r[4]-r[3]), which has bit k-1 set (if k >= 2).
// So bit k-1 of (r[3] - r[4]) mod 2^b:
// We need to check bit k of the add result before mod 2^k...
// Actually, let's use a different approach.
// Subtract and check: compute r[4] - r[3]. If result's MSB (bit k-1) is set,
// then r[4] < r[3] (with unsigned wraparound interpretation)... no.
// For unsigned comparison: r[3] > r[4] iff r[3] - r[4] (mod 2^b) in [1, 2^(b-1)-1]
// But we only care about k-bit values.
// Better: compute r[3] + (2^k - r[4]) = r[3] - r[4] + 2^k.
// If r[3] >= r[4]: result >= 2^k, so bit k is set.
// If r[3] < r[4]: result < 2^k, so bit k is clear.
// Compute 2^k - r[4] = NOT(r[4]) + 1 (k-bit complement), but we already have this.
// r[5] = r[3] + (2^k - r[4]) mod 2^b
// Actually: append_not(5, 4) gives ~r[4] in all B bits.
// We want only the k-bit complement: mask first.
append_and(4, 1, 2); // r[4] = second value, k bits
append_not(5, 4);
append_and(5, 5, 2); // k-bit NOT
append_add(5, 5, 6); // 2^k - r[4] (k-bit complement), but this gives 2^k if r[4]=0
append_add(5, 3, 5); // r[3] + 2^k - r[4]
// Bit k of r[5]: 1 if r[3] >= r[4], 0 if r[3] < r[4]
// If bit k = 1: min = r[4]. If bit k = 0: min = r[3].
append_right(7, 5, k); // shift bit k to bit 0
append_and(7, 7, 6); // isolate bit 0
// r[7] = 1 if r[3] >= r[4], else 0
// Spread to k-bit mask: subtract 1, then NOT, then AND with mask
// If r[7] = 1: r[7]-1 = 0, NOT = all 1s -> mask = all 1s in k bits
// If r[7] = 0: r[7]-1 = -1 = all 1s, NOT = 0 -> mask = 0 in k bits
// Build selection mask
// r[7] = 0 or 1. We want a k-bit mask that's all-1s if r[3] < r[4].
// r[3] < r[4] iff r[7] = 0.
// Negate r[7]: if 0 -> 1, if 1 -> 0
append_xor(7, 7, 6); // flip bit 0
append_and(7, 7, 6); // r[7] = 1 if r[3] < r[4], else 0
// Spread: r[7] - 1 = 0 or -1
// Actually: multiply by mask. Use: left shift and OR repeatedly.
// Or: subtract, then AND with mask.
// Simpler: use NOT and ADD to create mask
// r[7] = 0 or 1. subtract 1: if 1 -> 0, if 0 -> all 1s.
append_not(8, 6); // all 1s except bit 0 = -2 in two's complement
append_add(8, 7, 8); // r[7] - 1 = r[7] + (-1) ... no.
// r[7] + NOT(1) + 1... this is getting complex.
// Even simpler: if r[3] < r[4], pick r[3]; else pick r[4].
// r[0] = min(r[3], r[4]) = r[3] if r[3]<r[4] else r[4]
// Use conditional: min = r[3] XOR ((r[3] XOR r[4]) AND mask)
// where mask = all 1s if we should pick r[4].
// Let's just use: negate r[7] (0 or 1 -> 0...0 or 1...1 k-bit mask)
// r[7] = 1 iff r[3] < r[4] (select r[3])
// We want: r[0] = r[3] if r[7]=1, r[4] if r[7]=0
// = (r[3] AND expanded(r[7])) OR (r[4] AND expanded(NOT r[7]))
// Expand r[7] to k bits: left shift and OR
append_move(8, 7);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(9, 8, 1 << bit);
append_or(8, 8, 9);
}
append_and(8, 8, 2); // r[8] = k-bit mask
append_and(9, 3, 8); // r[3] if selected
append_not(10, 8);
append_and(10, 10, 2);
append_and(10, 4, 10); // r[4] if not selected
append_or(0, 9, 10); // r[0] = min(r[3], r[4])
}
} else {
// Full sort: implement bitonic sort or odd-even sort
// For simplicity, implement bubble sort with O(n^2) comparators
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - 1 - i; j++) {
// Compare-and-swap positions j and j+1
// Extract value at position j (bits j*k to (j+1)*k-1) and j+1
vector<bool> mask_j(B, false), mask_j1(B, false);
for (int b = 0; b < k; b++) {
mask_j[j * k + b] = true;
mask_j1[(j + 1) * k + b] = true;
}
append_store(2, mask_j);
append_store(3, mask_j1);
append_and(4, 0, 2); // value at j (in position j)
append_right(4, 4, j * k); // shift to position 0
append_and(5, 0, 3); // value at j+1 (in position j+1)
append_right(5, 5, (j+1) * k); // shift to position 0
// Compare r[4] and r[5] (both at position 0, k bits)
vector<bool> one_v(B, false);
one_v[0] = true;
append_store(6, one_v);
vector<bool> kmask(B, false);
for (int b = 0; b < k; b++) kmask[b] = true;
append_store(7, kmask);
// r[4] - r[5] + 2^k: check bit k for comparison
append_not(8, 5);
append_and(8, 8, 7);
append_add(8, 8, 6); // 2^k - r[5]
append_add(8, 4, 8); // r[4] - r[5] + 2^k
// Bit k: 1 if r[4] >= r[5] (need swap), 0 if r[4] < r[5] (no swap)
append_right(9, 8, k);
append_and(9, 9, 6); // r[9] = 1 if need swap
// If swap: r[0] = r[0] with positions j and j+1 swapped
// min goes to position j, max goes to position j+1
// Build XOR mask for swap
append_xor(10, 4, 5); // diff = r[4] XOR r[5]
// Expand r[9] to k bits
append_move(11, 9);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(12, 11, 1 << bit);
append_or(11, 11, 12);
}
append_and(11, 11, 7); // k-bit swap mask
append_and(10, 10, 11); // conditional diff
// XOR into both positions
append_left(12, 10, j * k);
append_left(13, 10, (j + 1) * k);
append_xor(0, 0, 12);
append_xor(0, 0, 13);
}
}
}
}
\end{lstlisting}
\section{Complexity Analysis}
\begin{itemize}
\item \textbf{Subtask 1 (Minimum):} $O(k \log n)$ instructions for the tournament approach with $\log n$ rounds.
\item \textbf{Subtask 2 (Full Sort):} $O(n^2 k)$ instructions for bubble sort. An optimized bitonic sort network would use $O(n \log^2 n \cdot k)$ instructions.
\item \textbf{Register count:} Uses approximately 15 registers.
\end{itemize}
\end{document}
#include <bits/stdc++.h>
using namespace std;
// IOI 2021 - Registers
// Use bitwise register operations to find minimum (s=0) or sort (s=1)
// n values of k bits packed in register 0 (b=2000 bit registers).
// Grader-provided functions
void append_move(int t, int x);
void append_store(int t, vector<bool> v);
void append_and(int t, int x, int y);
void append_or(int t, int x, int y);
void append_xor(int t, int x, int y);
void append_not(int t, int x);
void append_left(int t, int x, int p);
void append_right(int t, int x, int p);
void append_add(int t, int x, int y);
const int B = 2000;
void construct_instructions(int s, int n, int k, int /*q*/) {
// Create constant masks
vector<bool> mask_k(B, false);
for (int i = 0; i < k; i++) mask_k[i] = true;
vector<bool> one(B, false);
one[0] = true;
if (s == 0) {
// Find minimum using pairwise tournament
append_store(2, mask_k); // k-bit mask
for (int step = 1; step < n; step *= 2) {
// Shift to align pairs
append_right(1, 0, step * k);
// Extract both values to k bits
append_and(3, 0, 2);
append_and(4, 1, 2);
// Compare: compute r[3] + (2^k - r[4])
// If result has bit k set, r[3] >= r[4]; otherwise r[3] < r[4]
append_not(5, 4);
append_and(5, 5, 2); // k-bit complement of r[4]
append_store(6, one);
append_add(5, 5, 6); // 2^k - r[4]
append_add(5, 3, 5); // r[3] - r[4] + 2^k
// Extract comparison bit (bit k)
append_right(7, 5, k);
append_and(7, 7, 6); // r[7] = 1 if r[3] >= r[4]
// We want to select r[3] if r[3] < r[4] (r[7]=0), else r[4]
append_xor(7, 7, 6); // r[7] = 1 if r[3] < r[4]
append_and(7, 7, 6);
// Expand r[7] to k-bit selection mask
append_move(8, 7);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(9, 8, 1 << bit);
append_or(8, 8, 9);
}
append_and(8, 8, 2);
// Select: r[0] = (r[3] & mask) | (r[4] & ~mask)
append_and(9, 3, 8);
append_not(10, 8);
append_and(10, 10, 2);
append_and(10, 4, 10);
append_or(0, 9, 10);
}
} else {
// Full sort using bubble sort with register compare-and-swap
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - 1 - i; j++) {
// Masks for positions j and j+1
vector<bool> mask_j(B, false), mask_j1(B, false);
for (int b = 0; b < k; b++) {
mask_j[j * k + b] = true;
mask_j1[(j + 1) * k + b] = true;
}
append_store(2, mask_j);
append_store(3, mask_j1);
// Extract values at positions j and j+1, shift to position 0
append_and(4, 0, 2);
append_right(4, 4, j * k);
append_and(5, 0, 3);
append_right(5, 5, (j + 1) * k);
// Constants for comparison
vector<bool> one_v(B, false);
one_v[0] = true;
append_store(6, one_v);
vector<bool> kmask(B, false);
for (int b = 0; b < k; b++) kmask[b] = true;
append_store(7, kmask);
// Compare: r[4] - r[5] + 2^k
append_not(8, 5);
append_and(8, 8, 7);
append_add(8, 8, 6);
append_add(8, 4, 8);
// Bit k: 1 if r[4] >= r[5] (need swap)
append_right(9, 8, k);
append_and(9, 9, 6);
// Build conditional XOR-swap mask
append_xor(10, 4, 5); // diff between the two values
// Expand swap flag to k bits
append_move(11, 9);
for (int bit = 0; (1 << bit) < k; bit++) {
append_left(12, 11, 1 << bit);
append_or(11, 11, 12);
}
append_and(11, 11, 7);
append_and(10, 10, 11); // conditional diff
// Apply XOR swap to both positions
append_left(12, 10, j * k);
append_left(13, 10, (j + 1) * k);
append_xor(0, 0, 12);
append_xor(0, 0, 13);
}
}
}
}