ICPC 2024 - B. Bingo for the Win!
Each called number can only be claimed by the fastest player who still has an uncrossed copy of that number. After all n k copies from all sheets have been called in a uniformly random order, we must compute the proba...
Source-first archive entry
This page is built from the copied files in competitive_programming/icpc/2024/B-bingo-for-the-win. Edit
competitive_programming/icpc/2024/B-bingo-for-the-win/solution.tex to update the written solution and
competitive_programming/icpc/2024/B-bingo-for-the-win/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 B
Bingo for the Win!
Time limit: 1 second
Bingo is a game of chance for multiple players. Each player receives a sheet with some numbers, and
a game master then calls out these numbers in a random order. Players cross off the numbers that they
have heard, and the first player to cross off all their numbers wins the game. This basic version of the
game has a reputation for being, well, a bit sedate. No particular action is required of the players except
for not falling asleep.
In this problem we will analyze a more dynamic version of Bingo that requires quick thinking. In our
version, called Speed Bingo, the game master also calls out the numbers from the sheets in a random
order. However, whenever a number is called out, only the first player to signal that he or she has the
number is allowed to cross it off their sheet. If a player has the same number multiple times, only one
copy may be crossed off at a time. When multiple players have the same number(s) on their sheets, who-
ever has the fastest reaction time has an advantage in winning Speed Bingo. But how big an advantage?
That’s what we need your help to find out.
Formally, there are n players, each receiving a (possibly) different sheet of k (not necessarily distinct)
numbers. Player 1 is faster to react than player 2, who in turn is faster than player 3, and so on, with
player n being the slowest. Consider the following example, corresponding to the first sample input,
where three players receive four numbers each:
1 2 1 2 3 4
3 4 5 6 7 8
Player 1 Player 2 Player 3
When number “1” is called for the first time, player 1—being faster—will get to cross it off their sheet.
The second time “1” is called, player 2 will get to cross it off. So on average, we would expect player 1
to do better than players 2 and 3, since both of them will need some numbers that player 1 will get to first.
However, since players 2 and 3 have no numbers in common, their performances will be independent of
each other, even though player 2 is faster than player 3.
Suppose the game is played until all players have crossed off all of their numbers, that is, until all n · k
numbers on all of the sheets (including appropriate repetitions) have been read. Assuming the order of
the numbers is uniformly random, how likely is it for each player to finish last?
Input
The input describes a single game of Speed Bingo. The first line contains two integers n and k, the
number of players and number of numbers on each sheet (1 ≤ n ≤ 100, 1 ≤ k ≤ 1 000). This is
followed by n lines containing k integers each, where the ith line gives the numbers on the sheet for the
ith player. All those numbers are between 1 and 109 , inclusive.
Output
Output n lines, one for each player. The ith line should contain the probability that player i finishes last.
All values must be accurate to an absolute error of at most 10−6 .
48th ICPC World Championship Problem B: Bingo for the Win! © ICPC Foundation 3
Sample Input 1 Sample Output 1
3 4 0.000000000
1 2 3 4 0.500000000
1 2 5 6 0.500000000
3 4 7 8
Sample Input 2 Sample Output 2
4 2 0.250000000
1 2 0.250000000
3 4 0.250000000
10 5 0.250000000
7 8
48th ICPC World Championship Problem B: Bingo for the Win! © ICPC Foundation 4
Editorial
Rendered from the copied solution.tex file. The original TeX source remains
available below.
Key Observations
For one fixed value $x$, only the players containing $x$ matter. If the total multiplicity of $x$ over all sheets is $c_x$, then the $c_x$ calls of $x$ appear in a uniformly random order among all calls.
Let the slowest player containing $x$ be its owner. Every earlier copy of $x$ is eventually claimed by faster players, while the owner claims the final copies of $x$.
Therefore the owner finishes the number $x$ exactly at the last occurrence of $x$ in the global call order. Any faster player containing $x$ finishes their own copies of $x$ strictly before that.
So the whole game is decided by whichever distinct value appears for the last time latest. The player who owns that value is exactly the player who finishes last.
Algorithm
Scan all $n \cdot k$ sheet entries.
For every distinct value $x$, store:
its total multiplicity $c_x$ across all sheets,
the largest player index containing it, i.e. the owner of $x$.
Add $c_x$ to the owner's score.
Output, for player $i$, \[ \frac{\text{score}_i}{n \cdot k}. \] enumerate
Why The Formula Is So Simple
Fix a distinct value $x$ with total multiplicity $c_x$. Give every copy of every number an independent continuous random priority in $[0,1]$, and order calls by increasing priority. This is equivalent to a uniformly random permutation of all copies.
The last occurrence time of $x$ is then the maximum of $c_x$ independent uniform random variables, so its cumulative distribution function is \[ \Pr[L_x \le t] = t^{c_x}. \]
If a player owns several values, their finishing time is the maximum of the last-occurrence times of the values they own. Since different values use disjoint sets of copies, these maxima are independent across players. If player $i$ owns values with multiplicities summing to $s_i$, then \[ \Pr[F_i \le t] = t^{s_i}, \] so $F_i$ has density $s_i t^{s_i - 1}$.
Hence \[ \Pr[\text{player } i \text{ finishes last}] = \int_0^1 s_i t^{s_i - 1} \prod_{j \ne i} t^{s_j} \, dt = s_i \int_0^1 t^{\sum_j s_j - 1} \, dt = \frac{s_i}{\sum_j s_j}. \] But $\sum_j s_j = n \cdot k$, because every sheet entry belongs to exactly one owner's pile. Therefore the answer is simply $s_i / (n \cdot k)$.
Correctness Proof
We prove that the algorithm outputs the correct probability for every player.
Lemma 1.
For a fixed value $x$, the player who owns $x$ finishes their copies of $x$ at the last occurrence of $x$ in the global call order, and every other player containing $x$ finishes earlier.
Proof.
Whenever $x$ is called, the fastest player still needing $x$ claims that copy. Thus the copies of $x$ are claimed in increasing player-speed order. The slowest player containing $x$ is the last one who can still need $x$, so they receive the final copy of $x$, which happens exactly at the last occurrence of $x$. Every faster player containing $x$ must already have received all their copies before that final occurrence. □
Lemma 2.
The player who finishes last is exactly the owner of the value whose last occurrence is latest among all distinct values.
Proof.
By Lemma 1, for every value $x$, its owner finishes no earlier than the last occurrence of $x$, and any other player containing $x$ finishes strictly earlier than that last occurrence. Therefore if value $x$ has the latest last occurrence overall, its owner cannot finish before any other player. Conversely, if player $i$ finishes last, then one of the numbers on their sheet attains their finishing time; by Lemma 1, player $i$ must own that number, and its last occurrence is latest overall. □
Lemma 3.
If player $i$ owns entries whose total multiplicity is $s_i$, then the probability that player $i$ finishes last is $s_i / (n \cdot k)$.
Proof.
As shown above, the owner's finishing-time maximum has density $s_i t^{s_i-1}$, while the maxima for other players contribute a factor of $t^{s_j}$. Integrating gives \[ \Pr[i \text{ last}] = \frac{s_i}{\sum_j s_j} = \frac{s_i}{n \cdot k}. \] □
Theorem.
The algorithm outputs the correct probability for every player.
Proof.
The algorithm computes exactly $s_i$, the total multiplicity of values owned by player $i$, by summing the total multiplicity of each distinct value into its slowest owner. By Lemma 3, the correct answer is then $s_i / (n \cdot k)$, which is exactly what the algorithm prints. □
Complexity Analysis
We process each of the $n \cdot k$ sheet entries once and perform only hash-table updates. The running time is $O(nk)$ expected, and the memory usage is $O(D)$ where $D$ is the number of distinct values.
Implementation Notes
While scanning the sheets, the current player index is increasing, so the last player seen for a value is automatically its owner.
A value may appear multiple times on one sheet; all such copies count toward the same owner's total.
Printing with nine digits after the decimal point easily satisfies the required absolute error.
Code
Exact copied C++ implementation from solution.cpp.
#include <bits/stdc++.h>
using namespace std;
namespace {
void solve() {
int n, k;
cin >> n >> k;
unordered_map<long long, pair<int, int>> info;
info.reserve(static_cast<size_t>(n * k * 2));
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < k; ++j) {
long long x;
cin >> x;
auto& entry = info[x];
++entry.first;
entry.second = i;
}
}
vector<long long> owned(n + 1, 0);
for (const auto& it : info) {
const int total = it.second.first;
const int owner = it.second.second;
owned[owner] += total;
}
const double denom = static_cast<double>(n) * static_cast<double>(k);
cout << fixed << setprecision(9);
for (int i = 1; i <= n; ++i) {
cout << owned[i] / denom << '\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.
\documentclass[11pt]{article}
\usepackage[margin=1in]{geometry}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,amssymb,amsthm}
\usepackage{enumitem}
\title{ICPC World Finals 2024\\B. Bingo for the Win!}
\author{}
\date{}
\begin{document}
\maketitle
\section*{Problem Summary}
Each called number can only be claimed by the fastest player who still has an uncrossed copy of that
number. After all $n \cdot k$ copies from all sheets have been called in a uniformly random order, we must
compute the probability that each player is the one who finishes last.
\section*{Key Observations}
\begin{itemize}[leftmargin=*]
\item For one fixed value $x$, only the players containing $x$ matter. If the total multiplicity of $x$
over all sheets is $c_x$, then the $c_x$ calls of $x$ appear in a uniformly random order among all calls.
\item Let the slowest player containing $x$ be its \emph{owner}. Every earlier copy of $x$ is eventually
claimed by faster players, while the owner claims the final copies of $x$.
\item Therefore the owner finishes the number $x$ exactly at the \emph{last} occurrence of $x$ in the
global call order. Any faster player containing $x$ finishes their own copies of $x$ strictly before that.
\item So the whole game is decided by whichever distinct value appears for the last time latest. The
player who owns that value is exactly the player who finishes last.
\end{itemize}
\section*{Algorithm}
\begin{enumerate}[leftmargin=*]
\item Scan all $n \cdot k$ sheet entries.
\item For every distinct value $x$, store:
\begin{itemize}[leftmargin=*]
\item its total multiplicity $c_x$ across all sheets,
\item the largest player index containing it, i.e. the owner of $x$.
\end{itemize}
\item Add $c_x$ to the owner's score.
\item Output, for player $i$,
\[
\frac{\text{score}_i}{n \cdot k}.
\]
\end{enumerate}
\section*{Why The Formula Is So Simple}
Fix a distinct value $x$ with total multiplicity $c_x$. Give every copy of every number an independent
continuous random priority in $[0,1]$, and order calls by increasing priority. This is equivalent to a
uniformly random permutation of all copies.
The last occurrence time of $x$ is then the maximum of $c_x$ independent uniform random variables, so
its cumulative distribution function is
\[
\Pr[L_x \le t] = t^{c_x}.
\]
If a player owns several values, their finishing time is the maximum of the last-occurrence times of the
values they own. Since different values use disjoint sets of copies, these maxima are independent across
players. If player $i$ owns values with multiplicities summing to $s_i$, then
\[
\Pr[F_i \le t] = t^{s_i},
\]
so $F_i$ has density $s_i t^{s_i - 1}$.
Hence
\[
\Pr[\text{player } i \text{ finishes last}]
= \int_0^1 s_i t^{s_i - 1} \prod_{j \ne i} t^{s_j} \, dt
= s_i \int_0^1 t^{\sum_j s_j - 1} \, dt
= \frac{s_i}{\sum_j s_j}.
\]
But $\sum_j s_j = n \cdot k$, because every sheet entry belongs to exactly one owner's pile. Therefore
the answer is simply $s_i / (n \cdot k)$.
\section*{Correctness Proof}
We prove that the algorithm outputs the correct probability for every player.
\paragraph{Lemma 1.}
For a fixed value $x$, the player who owns $x$ finishes their copies of $x$ at the last occurrence of $x$
in the global call order, and every other player containing $x$ finishes earlier.
\paragraph{Proof.}
Whenever $x$ is called, the fastest player still needing $x$ claims that copy. Thus the copies of $x$ are
claimed in increasing player-speed order. The slowest player containing $x$ is the last one who can still
need $x$, so they receive the final copy of $x$, which happens exactly at the last occurrence of $x$.
Every faster player containing $x$ must already have received all their copies before that final occurrence.
\qed
\paragraph{Lemma 2.}
The player who finishes last is exactly the owner of the value whose last occurrence is latest among all
distinct values.
\paragraph{Proof.}
By Lemma 1, for every value $x$, its owner finishes no earlier than the last occurrence of $x$, and any
other player containing $x$ finishes strictly earlier than that last occurrence. Therefore if value $x$ has the
latest last occurrence overall, its owner cannot finish before any other player. Conversely, if player $i$
finishes last, then one of the numbers on their sheet attains their finishing time; by Lemma 1, player $i$
must own that number, and its last occurrence is latest overall. \qed
\paragraph{Lemma 3.}
If player $i$ owns entries whose total multiplicity is $s_i$, then the probability that player $i$ finishes last
is $s_i / (n \cdot k)$.
\paragraph{Proof.}
As shown above, the owner's finishing-time maximum has density $s_i t^{s_i-1}$, while the maxima for
other players contribute a factor of $t^{s_j}$. Integrating gives
\[
\Pr[i \text{ last}] = \frac{s_i}{\sum_j s_j} = \frac{s_i}{n \cdot k}.
\]
\qed
\paragraph{Theorem.}
The algorithm outputs the correct probability for every player.
\paragraph{Proof.}
The algorithm computes exactly $s_i$, the total multiplicity of values owned by player $i$, by summing the
total multiplicity of each distinct value into its slowest owner. By Lemma 3, the correct answer is then
$s_i / (n \cdot k)$, which is exactly what the algorithm prints. \qed
\section*{Complexity Analysis}
We process each of the $n \cdot k$ sheet entries once and perform only hash-table updates. The running
time is $O(nk)$ expected, and the memory usage is $O(D)$ where $D$ is the number of distinct values.
\section*{Implementation Notes}
\begin{itemize}[leftmargin=*]
\item While scanning the sheets, the current player index is increasing, so the last player seen for a
value is automatically its owner.
\item A value may appear multiple times on one sheet; all such copies count toward the same owner's
total.
\item Printing with nine digits after the decimal point easily satisfies the required absolute error.
\end{itemize}
\end{document}
#include <bits/stdc++.h>
using namespace std;
namespace {
void solve() {
int n, k;
cin >> n >> k;
unordered_map<long long, pair<int, int>> info;
info.reserve(static_cast<size_t>(n * k * 2));
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < k; ++j) {
long long x;
cin >> x;
auto& entry = info[x];
++entry.first;
entry.second = i;
}
}
vector<long long> owned(n + 1, 0);
for (const auto& it : info) {
const int total = it.second.first;
const int owner = it.second.second;
owned[owner] += total;
}
const double denom = static_cast<double>(n) * static_cast<double>(k);
cout << fixed << setprecision(9);
for (int i = 1; i <= n; ++i) {
cout << owned[i] / denom << '\n';
}
}
} // namespace
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
solve();
return 0;
}