ICPC 2023
ICPC 2023

K. Alea Iacta Est

47th Annual hosted by ICPC World Championship AASTMT Problem K Alea Iacta Est Time limit: 10 seconds...

Updated May 21, 2026
Track ICPC
Year 2023
Statement Text + PDF
TeXC++Statement textStatement PDF

Problem Statement

Formatted from the contest statement text, with sample tests broken out into copyable blocks.

  47th Annual                                                                               hosted by
  ICPC World
Championship                                                                                AASTMT
                                           Problem K
                                         Alea Iacta Est
                                    Time limit: 10 seconds
You play a game with multiple fair six-sided dice. Each die’s face displays a single symbol. The
objective of the game is to roll the dice and create a valid word from the symbols on top of each die. If
you cannot form a word, you may reroll the dice for another attempt.
Figure K.1: Five dice making a valid word corresponding to Sample Input 1.

Suppose there are five dice: one of them contains letters A, B, C, D, E, and P (abbreviated as ABCDEP), and the other dice contain letters AEHOXU, AISOLR, ABCDEF, and ABCSCC. The first roll yields the following letters on the tops of respective dice: P, X, R, E, and S. As it is impossible to arrange these letters into a valid word, you decide to keep the P, S, and E, and reroll the other dice, in an attempt to make words like PARSE, PAUSE, PHASE, POISE, PROSE, PULSE, or PURSE. The two dice yield E and A, resulting in the following five letters: P, E, A, E, and S. You still cannot think of a valid word, so you decide to keep four letters and reroll only the last die, which has three sides with letter C. By doing so, there is a 50% chance that it will be possible to make a final valid word: PEACE, as shown in Figure K.1. When you roll a die, it lands on any one of its faces with equal probability. What is the expected number of rolls needed to make a valid word, assuming you use an optimal strategy?

Input

The first line of input contains two numbers d and w, where d (1 ≤ d ≤ 6) is the number of dice and w (1 ≤ w ≤ 2 · 105 ) is the number of valid words in the dictionary. The following d lines each have 6 symbols, one for each face of the die. The final w lines contain w distinct valid words in the dictionary. Every word has exactly d symbols. All symbols in the input are either uppercase letters (A–Z) or digits (0–9).

Output

If it is possible to make a valid word, output the expected number of rolls needed to make a valid word when using an optimal strategy. Otherwise, output impossible. Your answer should have an absolute or relative error of at most 10−6 .

                                World Finals | ICPC 2023 Luxor
  47th Annual                                                            hosted by
  ICPC World
Championship                                                             AASTMT

Sample Tests

Sample 1
Sample Input
5 8
ABCDEP
AEHOXU
AISOLR
ABCDEF
ABCSCC
PARSE
PAUSE
PHASE
POISE
PROSE
PULSE
PURSE
PEACE
Sample Output
9.677887141
Sample 2
Sample Input
2 1
AAAAAA
BBBBBB
AB
Sample Output
1.0
Sample 3
Sample Input
3 1
123456
123456
123456
666
Sample Output
10.555444555
Sample 4
Sample Input
2 1
ABCDEF
GHI234
AB
Sample Output
impossible

Editorial

The solution write-up is rendered from the LaTeX source, with equations kept live through MathJax.

Key Observations

  • A state is a vector in $\{1,2,3,4,5,6,*\}^d$. A fixed state has no * symbols and describes one concrete roll outcome. An undetermined state has stars in the positions we are about to reroll.

  • The goal states are exactly the fixed states whose letters, after sorting, match some sorted dictionary word.

  • From a fixed state we may, at cost $0$, choose any nonempty subset of coordinates and replace them by stars. That moves us to an undetermined state.

  • If an undetermined state has $s$ stars, then one reroll replaces them uniformly by one of $6^s$ matching fixed states and costs $1$ roll.

  • This yields Bellman equations on a finite state graph. Running the relaxations backwards from goal states leads to a Dijkstra-like algorithm.

Algorithm

  1. Encode every state in base $7$, using digit $6$ for *. Precompute for each state whether it is fixed and how many stars it contains.

  2. Initialize distance $0$ for every goal fixed state and $\infty$ for all other states. Process states in increasing tentative distance with a priority queue.

  3. When a fixed state $x$ is finalized, update every undetermined state $y$ obtained by replacing a nonempty subset of coordinates of $x$ by stars.

  4. For one such $y$, let $D_y$ be the set of matching fixed states that have already been finalized. If $y$ has $s$ stars, then using only those finalized fixed states as stopping states gives candidate value \[ \frac{6^s + \sum_{z \in D_y} \mathrm{dist}[z]}{|D_y|}. \] Maintain this value incrementally by storing $|D_y|$ and the sum of finalized distances.

  5. When an undetermined state $y$ is finalized, update every matching fixed state $x$ with \[ \mathrm{dist}[x] \le \mathrm{dist}[y], \] because from $x$ we may immediately choose to reroll exactly the starred coordinates of $y$.

  6. The answer is the distance of the all-star state. If it stays infinite, output impossible.

Correctness Proof

We prove that the algorithm returns the correct answer.

Lemma 1.

For a fixed state $x$ and any nonempty subset of coordinates, moving from $x$ to the corresponding undetermined state $y$ has additional expected cost $0$.

Proof.

Choosing which dice to reroll is a free decision taken after observing the current roll. No new roll is performed yet, so the expected number of future rolls from $x$ after choosing that subset is exactly the expected number of future rolls from $y$. □

Lemma 2.

Let $y$ be an undetermined state with $s$ stars, and let $D$ be any nonempty set of matching fixed states. If we keep rerolling those starred coordinates until the outcome first lands in $D$, then the expected remaining number of rolls is \[ \frac{6^s + \sum_{z \in D}\mathrm{dist}[z]}{|D|}. \]

Proof.

Each roll of the $s$ starred dice is uniformly distributed over the $6^s$ matching fixed states, so the probability of landing in $D$ on one attempt is $|D|/6^s$. Therefore the expected number of attempts until the first success is $6^s/|D|$.

Conditioned on success, the reached state is uniformly distributed over $D$, so the expected future cost after that success is \[ \frac{1}{|D|}\sum_{z \in D}\mathrm{dist}[z]. \] Adding these two expectations gives the stated formula. □

Lemma 3.

When the algorithm finalizes a state, its recorded distance equals the true optimal expected number of future rolls from that state.

Proof.

We process states in nondecreasing tentative distance. For fixed states, every relaxation to them comes from an already finalized undetermined state by Lemma 1, so their tentative value is the best value obtainable from already optimal successor states.

For an undetermined state $y$, Lemma 2 shows that any strategy based on a set $D$ of already finalized matching fixed states yields exactly the candidate value maintained by the algorithm. Adding a not-yet finalized fixed state cannot improve the optimum before that state's own distance is known, because all finalized states have distance no larger than any unfinalized one. Thus the first time $y$ is extracted from the priority queue, its tentative value already matches the true optimum. The usual Dijkstra induction now applies to both kinds of states. □

Theorem.

The algorithm outputs the minimum expected number of rolls needed to form a dictionary word, or impossible if no word can ever be formed.

Proof.

By Lemma 3, every finalized distance is optimal. The all-star state represents the situation before the first roll, so its optimal expected future number of rolls is exactly the answer required by the statement. If its distance remains infinite, then no goal state is reachable under any strategy, so the correct output is impossible. □

Complexity Analysis

There are $7^d$ total states and at most $6^d$ fixed states. Since $d \le 6$, this is small enough to enumerate explicitly. Each state's relaxations are over subsets or completions of at most $d$ coordinates, so the total running time is polynomial in $7^d$ and easily fits the limit. The memory usage is $O(7^d)$.

Implementation Notes

  • Dictionary words are stored in sorted form, because the order of dice does not matter when checking whether a fixed state forms a word.

  • Distances are stored in floating point and printed with nine digits after the decimal point.

Code

C++ solution used for this page.

C++

Clean code view with a raw-file link when you want the original source.

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

namespace {

using ld = long double;

struct StateInfo {
    array<unsigned char, 6> digit{};
    unsigned char stars = 0;
    bool fixed = false;
};

int d;
vector<int> pow7;
vector<int> pow6;
vector<StateInfo> info_state;

void enumerate_matches(int id, int idx, int current, const vector<int>& positions,
                       const function<void(int)>& fn) {
    if (idx == static_cast<int>(positions.size())) {
        fn(current);
        return;
    }
    int pos = positions[idx];
    for (int value = 0; value < 6; ++value) {
        enumerate_matches(id, idx + 1, current + (value - 6) * pow7[pos], positions, fn);
    }
}

void solve() {
    int w;
    cin >> d >> w;

    vector<string> dice(d);
    for (int i = 0; i < d; ++i) {
        cin >> dice[i];
    }

    unordered_set<string> goals;
    goals.reserve(w * 2);
    for (int i = 0; i < w; ++i) {
        string word;
        cin >> word;
        sort(word.begin(), word.end());
        goals.insert(word);
    }

    pow7.assign(d + 1, 1);
    pow6.assign(d + 1, 1);
    for (int i = 1; i <= d; ++i) {
        pow7[i] = pow7[i - 1] * 7;
        pow6[i] = pow6[i - 1] * 6;
    }
    int total_states = pow7[d];
    info_state.assign(total_states, {});

    for (int id = 0; id < total_states; ++id) {
        int value = id;
        bool fixed = true;
        int stars = 0;
        for (int i = 0; i < d; ++i) {
            int digit = value % 7;
            value /= 7;
            info_state[id].digit[i] = static_cast<unsigned char>(digit);
            if (digit == 6) {
                fixed = false;
                ++stars;
            }
        }
        info_state[id].fixed = fixed;
        info_state[id].stars = static_cast<unsigned char>(stars);
    }

    const ld INF = 1e100L;
    vector<ld> dist(total_states, INF);
    vector<ld> partial_sum(total_states, 0);
    vector<int> partial_count(total_states, 0);
    vector<char> done(total_states, false);

    using Node = pair<ld, int>;
    priority_queue<Node, vector<Node>, greater<Node>> pq;

    for (int id = 0; id < total_states; ++id) {
        if (!info_state[id].fixed) {
            continue;
        }
        string current;
        current.reserve(d);
        for (int i = 0; i < d; ++i) {
            current.push_back(dice[i][info_state[id].digit[i]]);
        }
        sort(current.begin(), current.end());
        if (goals.count(current)) {
            dist[id] = 0;
            pq.push({0, id});
        }
    }

    while (!pq.empty()) {
        ld current_dist = pq.top().first;
        int id = pq.top().second;
        pq.pop();
        if (done[id] || fabsl(current_dist - dist[id]) > 1e-18L) {
            continue;
        }
        done[id] = true;

        if (info_state[id].fixed) {
            for (int mask = 1; mask < (1 << d); ++mask) {
                int next_id = id;
                for (int pos = 0; pos < d; ++pos) {
                    if ((mask >> pos) & 1) {
                        next_id += (6 - info_state[id].digit[pos]) * pow7[pos];
                    }
                }
                ++partial_count[next_id];
                partial_sum[next_id] += current_dist;
                int variants = pow6[info_state[next_id].stars];
                ld candidate = (variants + partial_sum[next_id]) / partial_count[next_id];
                if (candidate + 1e-18L < dist[next_id]) {
                    dist[next_id] = candidate;
                    pq.push({candidate, next_id});
                }
            }
        } else {
            vector<int> stars;
            stars.reserve(info_state[id].stars);
            for (int pos = 0; pos < d; ++pos) {
                if (info_state[id].digit[pos] == 6) {
                    stars.push_back(pos);
                }
            }
            enumerate_matches(
                id, 0, id, stars,
                [&](int fixed_id) {
                    if (current_dist + 1e-18L < dist[fixed_id]) {
                        dist[fixed_id] = current_dist;
                        pq.push(make_pair(current_dist, fixed_id));
                    }
                });
        }
    }

    int start_state = 0;
    for (int i = 0; i < d; ++i) {
        start_state += 6 * pow7[i];
    }
    if (dist[start_state] >= INF / 2) {
        cout << "impossible\n";
        return;
    }

    cout << fixed << setprecision(9) << static_cast<double>(dist[start_state]) << '\n';
}

}  // namespace

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

    solve();
    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