IOI 2003
IOI 2003

Reverse

Problem Statement Summary Given a sequence of N integers and Q reverse operations, each specifying a range [l, r], apply all reversals in order and output the final sequence. A secondary variant asks for the minimum n...

Updated May 21, 2026
Track IOI
Year 2003
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 a sequence of $N$ integers and $Q$ reverse operations, each specifying a range $[l, r]$, apply all reversals in order and output the final sequence.

A secondary variant asks for the minimum number of reversals to sort a permutation.

Solution 1: Simulation with Implicit-Key Treap

Approach

An implicit-key treap (or splay tree) supports range reversal in $O(\log N)$ amortized time via lazy propagation. Each node carries a rev flag. When pushed down, it swaps the left and right children and propagates the flag.

Operations

  • split(t, k, l, r): split tree $t$ into the first $k$ elements ($l$) and the rest ($r$).

  • merge(t, l, r): merge trees $l$ and $r$ by priority.

  • reverseRange(root, l, r): split off $[l, r]$, flip its rev flag, and merge back.

#include <bits/stdc++.h>
using namespace std;
mt19937 rng(42);

struct Node {
    int val, sz, pri;
    bool rev;
    Node *l, *r;
    Node(int v) : val(v), sz(1), pri(rng()), rev(false),
                  l(nullptr), r(nullptr) {}
};

int sz(Node* t) { return t ? t->sz : 0; }

void push(Node* t) {
    if (t && t->rev) {
        swap(t->l, t->r);
        if (t->l) t->l->rev ^= 1;
        if (t->r) t->r->rev ^= 1;
        t->rev = false;
    }
}

void upd(Node* t) {
    if (t) t->sz = 1 + sz(t->l) + sz(t->r);
}

void split(Node* t, int k, Node*& l, Node*& r) {
    if (!t) { l = r = nullptr; return; }
    push(t);
    if (sz(t->l) + 1 <= k) {
        split(t->r, k - sz(t->l) - 1, t->r, r);
        l = t;
    } else {
        split(t->l, k, l, t->l);
        r = t;
    }
    upd(t);
}

void merge(Node*& t, Node* l, Node* r) {
    push(l); push(r);
    if (!l || !r) { t = l ? l : r; return; }
    if (l->pri > r->pri) {
        merge(l->r, l->r, r);
        t = l;
    } else {
        merge(r->l, l, r->l);
        t = r;
    }
    upd(t);
}

void reverseRange(Node*& root, int l, int r) {
    Node *a, *b, *c;
    split(root, l - 1, a, b);
    split(b, r - l + 1, b, c);
    if (b) b->rev ^= 1;
    merge(b, b, c);
    merge(root, a, b);
}

void inorder(Node* t, vector<int>& res) {
    if (!t) return;
    push(t);
    inorder(t->l, res);
    res.push_back(t->val);
    inorder(t->r, res);
}

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

    int N;
    cin >> N;
    Node* root = nullptr;
    for (int i = 0; i < N; i++) {
        int x; cin >> x;
        Node* node = new Node(x);
        merge(root, root, node);
    }

    int Q;
    cin >> Q;
    while (Q--) {
        int l, r;
        cin >> l >> r;
        reverseRange(root, l, r);
    }

    vector<int> result;
    inorder(root, result);
    for (int i = 0; i < N; i++)
        cout << result[i] << (i + 1 < N ? ' ' : '\n');

    return 0;
}

Solution 2: Minimum Reversals to Sort (BFS)

For small $N$ ($N \le 8$), enumerate all permutation states via BFS.

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

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

    int N;
    cin >> N;
    vector<int> perm(N);
    for (int i = 0; i < N; i++) cin >> perm[i];

    vector<int> target(N);
    iota(target.begin(), target.end(), 1);
    if (perm == target) { cout << 0 << "\n"; return 0; }

    map<vector<int>, int> dist;
    queue<vector<int>> q;
    dist[perm] = 0;
    q.push(perm);

    while (!q.empty()) {
        auto cur = q.front(); q.pop();
        int d = dist[cur];
        for (int l = 0; l < N; l++) {
            for (int r = l + 1; r < N; r++) {
                auto next = cur;
                reverse(next.begin() + l, next.begin() + r + 1);
                if (next == target) {
                    cout << d + 1 << "\n";
                    return 0;
                }
                if (!dist.count(next)) {
                    dist[next] = d + 1;
                    q.push(next);
                }
            }
        }
    }

    return 0;
}

Complexity Analysis

  • Solution 1 (Treap): $O((N + Q) \log N)$ time, $O(N)$ space.

  • Solution 2 (BFS): $O(N! \cdot N^2)$ time, $O(N! \cdot N)$ space. Feasible only for $N \le 8$.

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 2003 - Reverse
// Given a sequence of N integers and Q reversal operations, each reversing
// a subarray [l, r] (1-indexed), output the final sequence.
// Uses an implicit-key treap with lazy reversal for O(log N) per operation.
// Complexity: O((N + Q) log N) time, O(N) space.

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

mt19937 rng(42);

struct Node {
    int val, sz, pri;
    bool rev;
    Node *l, *r;
    Node(int v) : val(v), sz(1), pri(rng()), rev(false), l(nullptr), r(nullptr) {}
};

int sz(Node* t) { return t ? t->sz : 0; }

void push(Node* t) {
    if (t && t->rev) {
        swap(t->l, t->r);
        if (t->l) t->l->rev ^= 1;
        if (t->r) t->r->rev ^= 1;
        t->rev = false;
    }
}

void upd(Node* t) {
    if (t) t->sz = 1 + sz(t->l) + sz(t->r);
}

// Split into first k elements (left) and the rest (right)
void split(Node* t, int k, Node*& l, Node*& r) {
    if (!t) { l = r = nullptr; return; }
    push(t);
    if (sz(t->l) + 1 <= k) {
        split(t->r, k - sz(t->l) - 1, t->r, r);
        l = t;
    } else {
        split(t->l, k, l, t->l);
        r = t;
    }
    upd(t);
}

void merge(Node*& t, Node* l, Node* r) {
    push(l);
    push(r);
    if (!l || !r) { t = l ? l : r; return; }
    if (l->pri > r->pri) {
        merge(l->r, l->r, r);
        t = l;
    } else {
        merge(r->l, l, r->l);
        t = r;
    }
    upd(t);
}

// Reverse the subarray [l, r] (1-indexed)
void reverseRange(Node*& root, int l, int r) {
    Node *a, *b, *c;
    split(root, l - 1, a, b);
    split(b, r - l + 1, b, c);
    if (b) b->rev ^= 1;
    merge(b, b, c);
    merge(root, a, b);
}

void inorder(Node* t, vector<int>& res) {
    if (!t) return;
    push(t);
    inorder(t->l, res);
    res.push_back(t->val);
    inorder(t->r, res);
}

// Clean up memory
void destroy(Node* t) {
    if (!t) return;
    destroy(t->l);
    destroy(t->r);
    delete t;
}

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

    int N;
    cin >> N;

    Node* root = nullptr;
    for (int i = 0; i < N; i++) {
        int x;
        cin >> x;
        Node* node = new Node(x);
        merge(root, root, node);
    }

    int Q;
    cin >> Q;
    while (Q--) {
        int l, r;
        cin >> l >> r;
        if (l < r) { // only reverse if range has more than 1 element
            reverseRange(root, l, r);
        }
    }

    vector<int> result;
    result.reserve(N);
    inorder(root, result);

    for (int i = 0; i < N; i++) {
        cout << result[i] << (i + 1 < N ? ' ' : '\n');
    }

    destroy(root);
    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