All ICPC entries
Competitive Programming

ICPC 2024 - K. Tower of noiHa

After the first k optimal moves of the classical Tower of Hanoi, Lucas's son takes the whole stack that is still on peg 0 and drops it on top of peg 2 in one illegal move. From that resulting possibly invalid state, w...

Source sync Apr 19, 2026
Track ICPC
Year 2024
Files TeX, C++, statement assets
Folder competitive_programming/icpc/2024/K-tower-of-noiha
ICPC2024TeXC++statement textstatement pdf

Source-first archive entry

This page is built from the copied files in competitive_programming/icpc/2024/K-tower-of-noiha. Edit competitive_programming/icpc/2024/K-tower-of-noiha/solution.tex to update the written solution and competitive_programming/icpc/2024/K-tower-of-noiha/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

Copied statement text kept beside the solution archive for direct reference.

Problem K
                                               Tower of noiHa
                                            Time limit: 1 second
Lucas believes that at six years old, his son is ready to learn some
basic algorithms. To start, he chose one of the most beautiful
techniques: recursion, and to illustrate it, he picked the well-
known recursion game: the Tower of Hanoi.
The Tower of Hanoi is a mathematical game consisting of three
rods and a number of disks of various diameters, which can slide            Tower of Hanoi via Wikimedia Commons, CC BY-SA 3.0

onto any rod. The puzzle begins with the disks stacked on the first
rod in order of size, the smallest at the top, thus approximating a conical shape. The objective of the
puzzle is to transfer the entire stack to the last rod, obeying the following rules:

     • Only one disk may be moved at a time.
     • Each move consists of taking the top disk from one of the stacks and placing it on top of another
       stack or on an empty rod.
     • No disk may be placed on top of a disk smaller than itself.

Lucas knows that the minimal number of moves required to solve a Tower of Hanoi puzzle is 2n − 1,
where n is the number of disks. What’s more, the optimal moves are unique, which means that n and
the number of moves that have been done uniquely represent the current state of the game, given that the
disks are always moved optimally.
Lucas was showing his son how to solve the game step by step. He has already done the first k optimal
moves. Since it will still take a while to finish, he took a short break to grab some snacks. Unfortunately,
when he came back, he found that his naughty little son has done a big “move”: knowing that the goal is
to transfer all disks from the first rod to the last rod, his son literally transferred “all disks from the first
rod to the last rod” in one move (without changing their respective order), see figure K.1.

                                                     Before the big “move”

                                                     After the big “move”

                    Figure K.1: Layout of the game before and after the son’s big “move”.

Lucas believes that he can still use this as a teaching opportunity. He decides to solve the game still using
only “valid” moves. However, he wonders what is the current minimum number of moves required to
solve the game. Since he is also busy dealing with his son, he needs your help!
Note that a “valid” move is still well-defined even if the given state is invalid. That is, you can only
move one top disk at a time, and you cannot place it on top of another disk that is smaller than it. In
particular, it is valid to put a disk of size a on top of a rod that contains a disk of size b (b < a) if the top
disk on this rod has size c (a < c).

48th ICPC World Championship Problem K: Tower of noiHa © ICPC Foundation                                                  21

Input

The first line contains an integer n (1 ≤ n ≤ 200 000), the number of disks in the game.
The second line contains an integer k (0 ≤ k ≤ 2n − 1), the number of optimal moves Lucas did prior
to the big move. Note that k is given in binary.

Output

Output one integer in binary, the minimum number of moves required to finish the game.

 Sample Input 1                                      Sample Output 1
 3                                                   0
 0

 Sample Input 2                                      Sample Output 2
 3                                                   110
 10

 Sample Input 3                                      Sample Output 3
 5                                                   11
 11011

48th ICPC World Championship Problem K: Tower of noiHa © ICPC Foundation                        22

Editorial

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

Key Observations

  • Before the illegal move, the state is a normal Hanoi state, so each peg is individually sorted from large to small.

  • After moving the whole stack from peg $0$ onto peg $2$, peg $0$ becomes empty and peg $2$ is the concatenation of two sorted stacks. Therefore peg $2$ has at most one inversion point. If there is no inversion, the state is already valid.

  • If there is an inversion, let $d$ be the first disk above it. Then $d$ is the unique bad disk: it sits on top of some smaller disks. Every disk larger than $d$ is already in a valid relative order and never needs to interact with the trapped smaller disks below $d$.

  • Split the disks smaller than $d$ into two groups:

    • $C$: the disks trapped below $d$ on peg $2$,

    • $A$: the remaining smaller disks.

    • The disks in $A$ form an ordinary valid Hanoi configuration, and the disks in $B=\{d,d+1,\dots,n\}$ also form an ordinary valid Hanoi configuration.

    • While $d$ has not moved yet, every move of a disk from $B$ requires all disks of $A$ to be stacked on the third peg. Hence the cost is: \[ \text{cost to place }A\text{ for the first }B\text{-move} + \text{one or two }B\text{-moves} + \text{possibly one transfer of the whole }A\text{ stack.} \]

    • In the unique shortest path from any valid Hanoi state to a corner state, the smallest disk moves within the first two moves. Thus, for the bad disk $d$:

      • either $d$ is the first disk of $B$ that moves,

      • or one larger disk moves first, and then $d$ moves second.

      • Once $d$ moves away from the trapped disks, the whole state becomes valid. From then on, the remaining answer is just the ordinary distance from a valid Hanoi state to the final corner.

      Algorithm

      1. Decode the positions after the first $k$ optimal moves. This is done by scanning the padded binary representation of $k$ from the largest disk to the smallest while maintaining the current $(\text{source},\text{aux},\text{destination})$ role permutation.

      2. Build the actual state after the illegal move: move every disk that was on peg $0$ onto the top of peg $2$.

      3. If peg $2$ has no inversion, the whole state is valid. Use the standard Hanoi corner-distance formula: \[ \text{scan disks from large to small, maintaining the current target peg.} \]

      4. Otherwise find the bad disk $d$, the trapped set $C$, the free-small set $A$, and the big set $B=\{d,\dots,n\}$.

      5. Compress $A$ and $B$ separately by relative order only. For each compressed valid Hanoi state we can:

        • compute the ordinary distance to a target corner,

        • compute the first move on the unique shortest path to a target corner.

        • If every disk of $B$ is already on peg $2$, the shortest path will never move $d$. Then try the only two possibilities:

          • move all of $A$ to peg $0$ and move $d$ to peg $1$,

          • move all of $A$ to peg $1$ and move $d$ to peg $0$.

          • In both cases the state becomes valid, so add the ordinary finishing distance and take the smaller answer.

          • Otherwise compute the first move of the valid configuration $B$ toward peg $2$.

            • If that first move is $d$, place all disks of $A$ on the spare peg required for that move, move $d$, and the state is now valid.

            • If a larger disk moves first, place all disks of $A$ for that first move, execute it, move the whole stack $A$ to the spare peg needed for the second move, and then move $d$. After that the state is valid.

            • Construct the resulting valid full configuration and add its ordinary finishing distance. enumerate

              Correctness Proof

              We prove that the algorithm outputs the minimum number of valid moves.

              Lemma 1.

              After the illegal move, peg $2$ contains at most one inversion point, so there is at most one bad disk.

              Proof.

              Before the illegal move, the disks on peg $2$ form a valid stack and the disks on peg $0$ form another valid stack. The illegal move concatenates these two valid stacks, preserving the internal order of each. Therefore every inversion can only occur at their boundary, so there is at most one first disk that lies on top of a smaller disk.

              Lemma 2.

              Let $d$ be the bad disk. Until $d$ moves, the disks in $C$ cannot move, and the disks in $A$ behave exactly like an ordinary valid Hanoi configuration.

              Proof.

              Every disk in $C$ lies below $d$ on peg $2$, so no disk in $C$ is accessible before $d$ moves. Every disk in $A$ lies above all disks of $B$ on its peg, hence the top disk among $A$ on each peg is exactly the same as in an ordinary valid Hanoi state. Because all disks in $A$ are smaller than every disk in $B$, the disks of $B$ act only as peg bases and do not change the legality of moves among $A$.

              Lemma 3.

              In the shortest path from a valid Hanoi state to a corner state, the smallest disk moves within the first two moves.

              Proof.

              Consider the standard recursive description of the unique shortest path. For the largest misplaced disk $x$, either:

              • the first move is already inside the smaller recursive subproblem, or

              • all smaller disks are already on the required spare peg, so disk $x$ moves immediately.

              • In the first case, recurse on the smaller subproblem. If this continues until disk $1$, then disk $1$ moves first. Otherwise the recursion stops at some disk $x>1$ because all smaller disks are already on the needed spare peg; then disk $x$ moves first, and immediately afterward the remaining smaller subproblem is a corner-to-corner transfer, whose first move is disk $1$. Thus disk $1$ moves no later than the second move.

              Lemma 4.

              If the valid configuration of $B$ is not already at the final corner, then before the state becomes valid again, either one move of $B$ is needed (moving $d$ first) or exactly two moves of $B$ are needed (one larger disk, then $d$).

              Proof.

              The first time the full state can become valid is exactly when $d$ moves away from the trapped disks of $C$. By Lemma 3, in the shortest path of the valid Hanoi configuration $B$ toward peg $2$, the smallest disk of $B$, namely $d$, moves within the first two moves. Therefore the state becomes valid after either one or two $B$-moves, unless $B$ is already entirely on peg $2$, which is handled separately.

              Lemma 5.

              For a fixed sequence of one or two $B$-moves before moving $d$, the minimum number of moves spent on $A$ is exactly what the algorithm computes.

              Proof.

              Before each move of a disk in $B$, every disk of $A$ must be stacked on the unique spare peg not used by that $B$-move; otherwise some disk of $A$ would block the source or destination peg. By Lemma 2, the disks in $A$ form an ordinary valid Hanoi state, so the minimum cost to place them on a given peg is the standard corner-distance. If there are two $B$-moves before moving $d$, then after the first one the whole stack $A$ must move from one spare peg to another, costing exactly $2^{|A|}-1$ moves, and then the second $B$-move costs one more move. No cheaper sequence can exist because every required peg placement is forced.

              Lemma 6.

              After the algorithm moves $d$, the constructed configuration is valid and is exactly the configuration reached by some shortest sequence from the initial invalid state.

              Proof.

              By construction, the trapped disks $C$ remain on peg $2$, the disks of $A$ are stacked on the spare peg needed for the move of $d$, and the disks of $B$ follow the first one or two moves of their unique shortest path. Therefore after $d$ moves, no disk lies on top of a smaller disk, so the state is valid. Lemma 5 shows that no shorter prefix can reach a valid state with the same required $B$-moves.

              Theorem.

              The algorithm outputs the minimum number of valid moves required to finish the game.

              Proof.

              If the illegal move leaves a valid state, the algorithm returns the ordinary Hanoi distance, which is optimal. Otherwise, by Lemma 4, any optimal solution must reach validity again by either the special ``$B$ already finished'' case or by moving $d$ after one or two forced $B$-moves. Lemma 5 gives the minimum possible prefix cost until that happens, and Lemma 6 shows that the algorithm constructs the resulting valid state exactly. From that point on, the remaining minimum cost is the ordinary Hanoi distance from a valid state to the final corner. Summing these two optimal parts gives the global optimum.

              Complexity Analysis

              All scans are linear in the number of disks. Therefore the running time is $O(n)$ and the memory usage is $O(n)$.

              Implementation Notes

              • The answer can have up to $n$ binary digits, so the implementation stores it as a custom non-negative big integer over 64-bit limbs.

              • The standard corner-distance formula only sets bits corresponding to powers of two, so building those distances is especially simple.

              • The first-move routine for a valid Hanoi state is implemented iteratively to avoid recursion depth issues for $n$ up to $200\,000$.

Code

Exact copied C++ implementation from solution.cpp.

C++ competitive_programming/icpc/2024/K-tower-of-noiha/solution.cpp

Exact copied implementation source.

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

namespace {

struct BigInt {
    vector<uint64_t> limbs;

    void trim() {
        while (!limbs.empty() && limbs.back() == 0) {
            limbs.pop_back();
        }
    }

    bool is_zero() const {
        return limbs.empty();
    }

    void set_bit(int bit) {
        const int word = bit >> 6;
        const int off = bit & 63;
        if ((int)limbs.size() <= word) {
            limbs.resize(word + 1, 0);
        }
        limbs[word] |= (uint64_t(1) << off);
    }

    void add_small(uint64_t value) {
        if (value == 0) {
            return;
        }
        size_t i = 0;
        uint64_t carry = value;
        while (carry != 0) {
            if (i == limbs.size()) {
                limbs.push_back(0);
            }
            const uint64_t before = limbs[i];
            limbs[i] += carry;
            carry = (limbs[i] < before ? 1 : 0);
            ++i;
        }
    }

    void add(const BigInt& other) {
        const size_t n = max(limbs.size(), other.limbs.size());
        limbs.resize(n, 0);
        uint64_t carry = 0;
        for (size_t i = 0; i < n; ++i) {
            const uint64_t a = limbs[i];
            const uint64_t b = (i < other.limbs.size() ? other.limbs[i] : 0);
            limbs[i] = a + b + carry;
            if (carry == 0) {
                carry = (limbs[i] < a || limbs[i] < b) ? 1 : 0;
            } else {
                carry = (limbs[i] <= a || limbs[i] <= b) ? 1 : 0;
            }
        }
        if (carry != 0) {
            limbs.push_back(carry);
        }
    }

    BigInt shifted(int bits) const {
        if (is_zero()) {
            return {};
        }
        const int word_shift = bits >> 6;
        const int bit_shift = bits & 63;
        BigInt out;
        out.limbs.assign(word_shift + limbs.size() + 1, 0);
        uint64_t carry = 0;
        for (size_t i = 0; i < limbs.size(); ++i) {
            const uint64_t x = limbs[i];
            out.limbs[word_shift + i] |= (x << bit_shift);
            if (bit_shift != 0) {
                out.limbs[word_shift + i + 1] |= (x >> (64 - bit_shift));
            }
        }
        out.trim();
        return out;
    }

    int compare(const BigInt& other) const {
        if (limbs.size() != other.limbs.size()) {
            return limbs.size() < other.limbs.size() ? -1 : 1;
        }
        for (int i = (int)limbs.size() - 1; i >= 0; --i) {
            if (limbs[i] != other.limbs[i]) {
                return limbs[i] < other.limbs[i] ? -1 : 1;
            }
        }
        return 0;
    }

    string to_binary() const {
        if (is_zero()) {
            return "0";
        }
        const int last = (int)limbs.size() - 1;
        int highest = 63;
        while (((limbs[last] >> highest) & 1ULL) == 0) {
            --highest;
        }
        string out;
        out.reserve(last * 64 + highest + 1);
        for (int bit = highest; bit >= 0; --bit) {
            out.push_back(char('0' + ((limbs[last] >> bit) & 1ULL)));
        }
        for (int i = last - 1; i >= 0; --i) {
            for (int bit = 63; bit >= 0; --bit) {
                out.push_back(char('0' + ((limbs[i] >> bit) & 1ULL)));
            }
        }
        return out;
    }
};

int other_peg(int a, int b) {
    return 3 - a - b;
}

BigInt standard_cost(const vector<int>& pos, int goal) {
    BigInt ans;
    int target = goal;
    for (int disk = (int)pos.size() - 1; disk >= 1; --disk) {
        if (pos[disk] != target) {
            ans.set_bit(disk - 1);
            target = other_peg(pos[disk], target);
        }
    }
    return ans;
}

struct Move {
    bool exists = false;
    int disk = 0;
    int from = 0;
    int to = 0;
};

Move first_move_to_corner(const vector<int>& pos, int goal) {
    vector<pair<int, int>> frames;
    int target = goal;
    for (int disk = (int)pos.size() - 1; disk >= 2; --disk) {
        if (pos[disk] == target) {
            continue;
        }
        frames.push_back({disk, target});
        target = other_peg(pos[disk], target);
    }

    if ((int)pos.size() == 1) {
        return {};
    }
    if (pos[1] != target) {
        return {true, 1, pos[1], target};
    }
    if (frames.empty()) {
        return {};
    }
    const int disk = frames.back().first;
    const int dest = frames.back().second;
    return {true, disk, pos[disk], dest};
}

BigInt solve_case(int n, const string& k_bits) {
    string bits(n, '0');
    const int offset = n - (int)k_bits.size();
    for (int i = 0; i < (int)k_bits.size(); ++i) {
        bits[offset + i] = k_bits[i];
    }

    vector<int> before_pos(n + 1, 0);
    int src = 0;
    int aux = 1;
    int dst = 2;
    for (int disk = n, idx = 0; disk >= 1; --disk, ++idx) {
        if (bits[idx] == '0') {
            before_pos[disk] = src;
            const int new_aux = dst;
            dst = aux;
            aux = new_aux;
        } else {
            before_pos[disk] = dst;
            const int new_src = aux;
            aux = src;
            src = new_src;
        }
    }

    vector<int> rod0;
    vector<int> rod1;
    vector<int> rod2;
    rod0.reserve(n);
    rod1.reserve(n);
    rod2.reserve(n);
    for (int disk = n; disk >= 1; --disk) {
        if (before_pos[disk] == 0) {
            rod0.push_back(disk);
        } else if (before_pos[disk] == 1) {
            rod1.push_back(disk);
        } else {
            rod2.push_back(disk);
        }
    }
    rod2.insert(rod2.end(), rod0.begin(), rod0.end());
    rod0.clear();

    vector<int> pos(n + 1, 0);
    for (int disk : rod1) {
        pos[disk] = 1;
    }
    for (int disk : rod2) {
        pos[disk] = 2;
    }

    int bad = 0;
    for (int i = 0; i + 1 < (int)rod2.size(); ++i) {
        if (rod2[i] < rod2[i + 1]) {
            bad = rod2[i + 1];
            break;
        }
    }

    if (bad == 0) {
        return standard_cost(pos, 2);
    }

    vector<char> trapped(bad, false);
    int trapped_small = 0;
    for (int disk : rod2) {
        if (disk == bad) {
            break;
        }
        if (disk < bad) {
            trapped[disk] = true;
            ++trapped_small;
        }
    }

    vector<int> a_disks;
    a_disks.reserve(bad - 1 - trapped_small);
    for (int disk = 1; disk < bad; ++disk) {
        if (!trapped[disk]) {
            a_disks.push_back(disk);
        }
    }

    const int a_size = (int)a_disks.size();
    vector<int> pos_a(a_size + 1, 0);
    for (int i = 0; i < a_size; ++i) {
        pos_a[i + 1] = pos[a_disks[i]];
    }

    const int b_size = n - bad + 1;
    vector<int> pos_b(b_size + 1, 0);
    for (int disk = bad; disk <= n; ++disk) {
        pos_b[disk - bad + 1] = pos[disk];
    }

    auto build_full = [&](const vector<int>& current_b, int peg_a) {
        vector<int> full(n + 1, 0);
        for (int disk = 1; disk < bad; ++disk) {
            full[disk] = trapped[disk] ? 2 : peg_a;
        }
        for (int disk = bad; disk <= n; ++disk) {
            full[disk] = current_b[disk - bad + 1];
        }
        return full;
    };

    const BigInt cost_b_goal = standard_cost(pos_b, 2);
    if (cost_b_goal.is_zero()) {
        BigInt best;
        bool has_best = false;
        for (int peg_a : {0, 1}) {
            const int peg_d = other_peg(2, peg_a);
            vector<int> moved_b = pos_b;
            moved_b[1] = peg_d;

            BigInt total = standard_cost(pos_a, peg_a);
            total.add_small(1);
            total.add(standard_cost(build_full(moved_b, peg_a), 2));

            if (!has_best || total.compare(best) < 0) {
                best = total;
                has_best = true;
            }
        }
        return best;
    }

    const Move first = first_move_to_corner(pos_b, 2);
    const int init_spare = other_peg(first.from, first.to);
    BigInt answer = standard_cost(pos_a, init_spare);

    vector<int> after_b = pos_b;
    after_b[first.disk] = first.to;

    int final_spare = init_spare;
    if (first.disk == 1) {
        answer.add_small(1);
    } else {
        const Move second = first_move_to_corner(after_b, 2);
        after_b[second.disk] = second.to;
        final_spare = other_peg(second.from, second.to);
        answer.set_bit(a_size);
        answer.add_small(1);
    }

    answer.add(standard_cost(build_full(after_b, final_spare), 2));
    return answer;
}

void solve() {
    int n;
    string k;
    cin >> n >> k;
    cout << solve_case(n, k).to_binary() << '\n';
}

}  // namespace

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

    solve();
    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/icpc/2024/K-tower-of-noiha/solution.tex

Exact copied write-up source.

Raw file
\documentclass[11pt]{article}
\usepackage[margin=1in]{geometry}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,amssymb,amsthm}
\usepackage{enumitem}

\title{ICPC World Finals 2024\\K. Tower of noiHa}
\author{}
\date{}

\begin{document}
\maketitle

\section*{Problem Summary}

After the first $k$ optimal moves of the classical Tower of Hanoi, Lucas's son takes the whole stack that is still on peg $0$ and drops it on top of peg $2$ in one illegal move. From that resulting possibly invalid state, we must compute the minimum number of \emph{valid} moves needed to finish the game, and print the answer in binary.

\section*{Key Observations}

\begin{itemize}[leftmargin=*]
    \item Before the illegal move, the state is a normal Hanoi state, so each peg is individually sorted from large to small.
    \item After moving the whole stack from peg $0$ onto peg $2$, peg $0$ becomes empty and peg $2$ is the concatenation of two sorted stacks. Therefore peg $2$ has at most one inversion point. If there is no inversion, the state is already valid.
    \item If there is an inversion, let $d$ be the first disk above it. Then $d$ is the unique \emph{bad disk}: it sits on top of some smaller disks. Every disk larger than $d$ is already in a valid relative order and never needs to interact with the trapped smaller disks below $d$.
    \item Split the disks smaller than $d$ into two groups:
    \begin{itemize}[leftmargin=*]
        \item $C$: the disks trapped below $d$ on peg $2$,
        \item $A$: the remaining smaller disks.
    \end{itemize}
    The disks in $A$ form an ordinary valid Hanoi configuration, and the disks in $B=\{d,d+1,\dots,n\}$ also form an ordinary valid Hanoi configuration.
    \item While $d$ has not moved yet, every move of a disk from $B$ requires all disks of $A$ to be stacked on the third peg. Hence the cost is:
    \[
        \text{cost to place }A\text{ for the first }B\text{-move}
        + \text{one or two }B\text{-moves}
        + \text{possibly one transfer of the whole }A\text{ stack.}
    \]
    \item In the unique shortest path from any valid Hanoi state to a corner state, the smallest disk moves within the first two moves. Thus, for the bad disk $d$:
    \begin{itemize}[leftmargin=*]
        \item either $d$ is the first disk of $B$ that moves,
        \item or one larger disk moves first, and then $d$ moves second.
    \end{itemize}
    \item Once $d$ moves away from the trapped disks, the whole state becomes valid. From then on, the remaining answer is just the ordinary distance from a valid Hanoi state to the final corner.
\end{itemize}

\section*{Algorithm}

\begin{enumerate}[leftmargin=*]
    \item Decode the positions after the first $k$ optimal moves. This is done by scanning the padded binary representation of $k$ from the largest disk to the smallest while maintaining the current $(\text{source},\text{aux},\text{destination})$ role permutation.
    \item Build the actual state after the illegal move: move every disk that was on peg $0$ onto the top of peg $2$.
    \item If peg $2$ has no inversion, the whole state is valid. Use the standard Hanoi corner-distance formula:
    \[
        \text{scan disks from large to small, maintaining the current target peg.}
    \]
    \item Otherwise find the bad disk $d$, the trapped set $C$, the free-small set $A$, and the big set $B=\{d,\dots,n\}$.
    \item Compress $A$ and $B$ separately by relative order only. For each compressed valid Hanoi state we can:
    \begin{itemize}[leftmargin=*]
        \item compute the ordinary distance to a target corner,
        \item compute the first move on the unique shortest path to a target corner.
    \end{itemize}
    \item If every disk of $B$ is already on peg $2$, the shortest path will never move $d$. Then try the only two possibilities:
    \begin{itemize}[leftmargin=*]
        \item move all of $A$ to peg $0$ and move $d$ to peg $1$,
        \item move all of $A$ to peg $1$ and move $d$ to peg $0$.
    \end{itemize}
    In both cases the state becomes valid, so add the ordinary finishing distance and take the smaller answer.
    \item Otherwise compute the first move of the valid configuration $B$ toward peg $2$.
    \begin{itemize}[leftmargin=*]
        \item If that first move is $d$, place all disks of $A$ on the spare peg required for that move, move $d$, and the state is now valid.
        \item If a larger disk moves first, place all disks of $A$ for that first move, execute it, move the whole stack $A$ to the spare peg needed for the second move, and then move $d$. After that the state is valid.
    \end{itemize}
    \item Construct the resulting valid full configuration and add its ordinary finishing distance.
\end{enumerate}

\section*{Correctness Proof}

We prove that the algorithm outputs the minimum number of valid moves.

\paragraph{Lemma 1.}
After the illegal move, peg $2$ contains at most one inversion point, so there is at most one bad disk.

\paragraph{Proof.}
Before the illegal move, the disks on peg $2$ form a valid stack and the disks on peg $0$ form another valid stack. The illegal move concatenates these two valid stacks, preserving the internal order of each. Therefore every inversion can only occur at their boundary, so there is at most one first disk that lies on top of a smaller disk. \qed

\paragraph{Lemma 2.}
Let $d$ be the bad disk. Until $d$ moves, the disks in $C$ cannot move, and the disks in $A$ behave exactly like an ordinary valid Hanoi configuration.

\paragraph{Proof.}
Every disk in $C$ lies below $d$ on peg $2$, so no disk in $C$ is accessible before $d$ moves. Every disk in $A$ lies above all disks of $B$ on its peg, hence the top disk among $A$ on each peg is exactly the same as in an ordinary valid Hanoi state. Because all disks in $A$ are smaller than every disk in $B$, the disks of $B$ act only as peg bases and do not change the legality of moves among $A$. \qed

\paragraph{Lemma 3.}
In the shortest path from a valid Hanoi state to a corner state, the smallest disk moves within the first two moves.

\paragraph{Proof.}
Consider the standard recursive description of the unique shortest path. For the largest misplaced disk $x$, either:
\begin{itemize}[leftmargin=*]
    \item the first move is already inside the smaller recursive subproblem, or
    \item all smaller disks are already on the required spare peg, so disk $x$ moves immediately.
\end{itemize}
In the first case, recurse on the smaller subproblem. If this continues until disk $1$, then disk $1$ moves first. Otherwise the recursion stops at some disk $x>1$ because all smaller disks are already on the needed spare peg; then disk $x$ moves first, and immediately afterward the remaining smaller subproblem is a corner-to-corner transfer, whose first move is disk $1$. Thus disk $1$ moves no later than the second move. \qed

\paragraph{Lemma 4.}
If the valid configuration of $B$ is not already at the final corner, then before the state becomes valid again, either one move of $B$ is needed (moving $d$ first) or exactly two moves of $B$ are needed (one larger disk, then $d$).

\paragraph{Proof.}
The first time the full state can become valid is exactly when $d$ moves away from the trapped disks of $C$. By Lemma 3, in the shortest path of the valid Hanoi configuration $B$ toward peg $2$, the smallest disk of $B$, namely $d$, moves within the first two moves. Therefore the state becomes valid after either one or two $B$-moves, unless $B$ is already entirely on peg $2$, which is handled separately. \qed

\paragraph{Lemma 5.}
For a fixed sequence of one or two $B$-moves before moving $d$, the minimum number of moves spent on $A$ is exactly what the algorithm computes.

\paragraph{Proof.}
Before each move of a disk in $B$, every disk of $A$ must be stacked on the unique spare peg not used by that $B$-move; otherwise some disk of $A$ would block the source or destination peg. By Lemma 2, the disks in $A$ form an ordinary valid Hanoi state, so the minimum cost to place them on a given peg is the standard corner-distance. If there are two $B$-moves before moving $d$, then after the first one the whole stack $A$ must move from one spare peg to another, costing exactly $2^{|A|}-1$ moves, and then the second $B$-move costs one more move. No cheaper sequence can exist because every required peg placement is forced. \qed

\paragraph{Lemma 6.}
After the algorithm moves $d$, the constructed configuration is valid and is exactly the configuration reached by some shortest sequence from the initial invalid state.

\paragraph{Proof.}
By construction, the trapped disks $C$ remain on peg $2$, the disks of $A$ are stacked on the spare peg needed for the move of $d$, and the disks of $B$ follow the first one or two moves of their unique shortest path. Therefore after $d$ moves, no disk lies on top of a smaller disk, so the state is valid. Lemma 5 shows that no shorter prefix can reach a valid state with the same required $B$-moves. \qed

\paragraph{Theorem.}
The algorithm outputs the minimum number of valid moves required to finish the game.

\paragraph{Proof.}
If the illegal move leaves a valid state, the algorithm returns the ordinary Hanoi distance, which is optimal. Otherwise, by Lemma 4, any optimal solution must reach validity again by either the special ``$B$ already finished'' case or by moving $d$ after one or two forced $B$-moves. Lemma 5 gives the minimum possible prefix cost until that happens, and Lemma 6 shows that the algorithm constructs the resulting valid state exactly. From that point on, the remaining minimum cost is the ordinary Hanoi distance from a valid state to the final corner. Summing these two optimal parts gives the global optimum. \qed

\section*{Complexity Analysis}

All scans are linear in the number of disks. Therefore the running time is $O(n)$ and the memory usage is $O(n)$.

\section*{Implementation Notes}

\begin{itemize}[leftmargin=*]
    \item The answer can have up to $n$ binary digits, so the implementation stores it as a custom non-negative big integer over 64-bit limbs.
    \item The standard corner-distance formula only sets bits corresponding to powers of two, so building those distances is especially simple.
    \item The first-move routine for a valid Hanoi state is implemented iteratively to avoid recursion depth issues for $n$ up to $200\,000$.
\end{itemize}

\end{document}