ICPC 2023
ICPC 2023

C. Three Kinds of Dice

47th Annual hosted by ICPC World Championship AASTMT Problem C Three Kinds of Dice Time limit: 1 sec...

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 C
                                   Three Kinds of Dice
                                      Time limit: 1 second
See how they roll! According to a famous story, Warren Buffett
once challenged Bill Gates to a simple game of dice. He had three
dice; the first player could examine them and choose one of the
three. The second player would then choose one of the remaining
dice, and both players would roll their dice against each other,
aiming for the highest numbers. Warren offered to let Bill go
first, but this made Bill suspicious so he opted to go second. It
turned out to be a wise choice: these were intransitive dice. The
first die had an advantage when rolling against the second, the                             Image from rawpixel, CC0

second had an advantage when rolling against the third, but the first did not have an advantage when rolling against the third! To formalize this: define a “die” as any shape with at least one face such that each face shows a positive integer. When a die is rolled, one of its faces is selected uniformly at random. When two dice roll against each other, the die whose selected face shows a higher number earns 1 point; if both numbers are equal, each die earns 12 points. For dice D and D0 , define score(D, D0 ) as the expected number of points D earns from a single roll against D0 . If score(D, D0 ) > 21 , we say that D has an advantage over D0 ; if score(D, D0 ) = 21 , the two dice are tied. For example, if D is the first die in the sample input and D0 is the second, score(D, D0 ) = 94 and score(D0 , D) = 59 , so D0 has an advantage over D. Given two dice D1 and D2 such that D1 has an advantage over D2 , you want a third die D3 that forms an intransitive trio with the other two. Among all D3 that have an advantage over or tie with D1 , compute the lowest possible score(D3 , D2 ). If this is less than 12 , you can make an intransitive trio! Similarly, among all D3 such that D2 has an advantage over or ties with D3 , compute the highest possible score(D3 , D1 ).

Input

The input contains two lines, each describing one die. One of the dice (the first or the second) has an advantage over the other. The die with the advantage is D1 and the other is D2 . The first integer on a line gives n (1 ≤ n ≤ 105 ), the number of faces on the die. Then follow n integers fi (1 ≤ fi ≤ 109 for each 1 ≤ i ≤ n), giving the integer on each face.

Output

Output one line containing the lowest score(D3 , D2 ) and the highest score(D3 , D1 ) under the above conditions. The two scores do not need to use the same die D3 . Your answer should have an absolute error of at most 10−6 .

Sample Tests

Sample 1
Sample Input
 6 1 1 6 6 8 8
 3 2 4 9


                                  World Finals | ICPC 2
    47th Annual
    ICPC World
  Championship
Sample Output
0.291666667 0.750000000



023 Luxor
hosted by

AASTMT
Sample 2
Sample Input
4 9 3 7 5
3 4 2 3
Sample Output
0.500000000 0.500000000

Editorial

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

Key Observations

  • Fix one face value $v$ of $D_3$. Its contribution against $D_1$ and $D_2$ is the point \[ p(v)=\bigl(\text{score}(D_1,v),\ \text{score}(D_2,v)\bigr), \] where $\text{score}(D_i,v)$ is the expected score of $D_i$ against a deterministic one-face die showing $v$.

  • A general die $D_3$ is just a multiset of face values, so the pair \[ \bigl(\text{score}(D_1,D_3),\ \text{score}(D_2,D_3)\bigr) \] is a convex combination of points $p(v)$. Thus the set of all achievable pairs is exactly the convex hull of the candidate points.

  • The point $p(v)$ changes only when $v$ crosses a face value of $D_1$ or $D_2$. So it is enough to consider $1$, $\max+1$, and for every face value $x$ appearing on either die also $x$ and $x-1$.

  • Once the convex hull is known, the required extrema are intersections of that hull with the half-planes $x \le 1/2$ and $y \ge 1/2$.

Algorithm

  1. Sort both dice. If necessary, swap them so that the first one is the stronger die.

  2. Enumerate all candidate face values $v$ described above.

  3. For each $v$, compute $p(v)$ by binary searching in the two sorted dice.

  4. Sort these points by $x$ and keep, for each equal $x$, only the largest $y$.

  5. Build the upper convex hull of the remaining points.

  6. Scan the hull:

    • maximize $y$ over the part with $x \le 1/2$;

    • minimize $x$ over the part with $y \ge 1/2$;

    • if an edge crosses one of those threshold lines, interpolate on that edge.

    • Convert back using $\text{score}(D_3,D_i)=1-\text{score}(D_i,D_3)$. enumerate

      Correctness Proof

      We prove that the algorithm returns the correct answer.

      Lemma 1.

      For every possible die $D_3$, the pair \[ \bigl(\text{score}(D_1,D_3),\ \text{score}(D_2,D_3)\bigr) \] lies in the convex hull of the candidate points.

      Proof.

      If the faces of $D_3$ are $v_1,\dots,v_m$, then \[ \text{score}(D_i,D_3)=\frac{1}{m}\sum_{j=1}^{m}\text{score}(D_i,v_j) \] for $i=1,2$. Hence the pair for $D_3$ is the average of the points $p(v_j)$, so it lies in the convex hull. □

      Lemma 2.

      Every point in the convex hull of the candidate points is achievable by some die $D_3$.

      Proof.

      The candidate points themselves are produced by deterministic one-face dice. Any convex combination with rational coefficients can be realized by taking the corresponding number of copies of each face value. Therefore the whole convex hull is achievable. □

      Lemma 3.

      It is sufficient to consider only the candidate face values listed by the algorithm.

      Proof.

      For a fixed die $D_i$, the value $\text{score}(D_i,v)$ depends only on how many faces of $D_i$ are less than $v$ and how many are equal to $v$. These counts change only when $v$ crosses a face value already present on $D_1$ or $D_2$. Thus every open interval between consecutive critical values yields the same point, so one representative per interval is enough. □

      Theorem.

      The algorithm outputs the correct two extremal scores.

      Proof.

      By Lemmas 1 and 2, the achievable region for \[ \bigl(\text{score}(D_1,D_3),\ \text{score}(D_2,D_3)\bigr) \] is exactly the convex hull of the candidate points. Therefore minimizing $\text{score}(D_3,D_2)$ under $\text{score}(D_3,D_1)\ge 1/2$ is equivalent to maximizing $\text{score}(D_2,D_3)$ under $\text{score}(D_1,D_3)\le 1/2$, which is precisely the highest point of the hull inside $x \le 1/2$. The second answer is symmetric.

      The optimum of a linear objective over a convex polygon is attained either at a vertex or on an edge crossing the boundary line. Thus scanning the upper hull and interpolating on crossing edges yields the correct values. □

      Complexity Analysis

      Let $n_1$ and $n_2$ be the numbers of faces on the two given dice. We create $O(n_1+n_2)$ candidate values. Each candidate point is evaluated with binary searches in the sorted dice, so the total running time is $O((n_1+n_2)\log(n_1+n_2))$. The memory usage is $O(n_1+n_2)$.

      Implementation Notes

      • The implementation first ensures the first die is the stronger one, so the output formula is always the same.

      • long double is sufficient for the required precision.

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 Point {
    ld x = 0;
    ld y = 0;
};

ld cross(const Point& a, const Point& b, const Point& c) {
    return (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x);
}

ld opponent_score(const vector<long long>& die, long long value) {
    auto lo = lower_bound(die.begin(), die.end(), value);
    auto hi = upper_bound(die.begin(), die.end(), value);
    long long greater = die.end() - hi;
    long long equal = hi - lo;
    return (greater + 0.5L * equal) / static_cast<ld>(die.size());
}

ld duel_score(const vector<long long>& first, const vector<long long>& second) {
    long long wins = 0;
    long long ties = 0;
    for (long long x : first) {
        wins += lower_bound(second.begin(), second.end(), x) - second.begin();
        auto range = equal_range(second.begin(), second.end(), x);
        ties += range.second - range.first;
    }
    ld total = static_cast<ld>(first.size()) * second.size();
    return (wins + 0.5L * ties) / total;
}

void solve() {
    vector<long long> a, b;
    int n;

    cin >> n;
    a.resize(n);
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
    }
    cin >> n;
    b.resize(n);
    for (int i = 0; i < n; ++i) {
        cin >> b[i];
    }

    sort(a.begin(), a.end());
    sort(b.begin(), b.end());
    if (duel_score(a, b) < 0.5L) {
        swap(a, b);
    }

    vector<long long> candidates = {1};
    long long max_value = max(a.back(), b.back());
    candidates.push_back(max_value + 1);
    for (long long x : a) {
        candidates.push_back(x);
        if (x > 1) {
            candidates.push_back(x - 1);
        }
    }
    for (long long x : b) {
        candidates.push_back(x);
        if (x > 1) {
            candidates.push_back(x - 1);
        }
    }
    sort(candidates.begin(), candidates.end());
    candidates.erase(unique(candidates.begin(), candidates.end()), candidates.end());

    vector<Point> points;
    points.reserve(candidates.size());
    for (long long value : candidates) {
        points.push_back({opponent_score(a, value), opponent_score(b, value)});
    }

    sort(points.begin(), points.end(), [](const Point& lhs, const Point& rhs) {
        if (lhs.x != rhs.x) {
            return lhs.x < rhs.x;
        }
        return lhs.y < rhs.y;
    });

    vector<Point> filtered;
    for (const Point& p : points) {
        if (!filtered.empty() && fabsl(filtered.back().x - p.x) <= 1e-18L) {
            filtered.back().y = max(filtered.back().y, p.y);
        } else {
            filtered.push_back(p);
        }
    }

    vector<Point> hull;
    for (const Point& p : filtered) {
        while (hull.size() >= 2 && cross(hull[hull.size() - 2], hull.back(), p) >= -1e-18L) {
            hull.pop_back();
        }
        hull.push_back(p);
    }

    ld best_y = -1;
    ld best_x = 2;
    for (int i = 0; i < static_cast<int>(hull.size()); ++i) {
        if (hull[i].x <= 0.5L + 1e-18L) {
            best_y = max(best_y, hull[i].y);
        }
        if (hull[i].y >= 0.5L - 1e-18L) {
            best_x = min(best_x, hull[i].x);
        }
        if (i + 1 == static_cast<int>(hull.size())) {
            continue;
        }

        const Point& p = hull[i];
        const Point& q = hull[i + 1];
        if ((p.x - 0.5L) * (q.x - 0.5L) < -1e-18L) {
            ld ratio = (0.5L - p.x) / (q.x - p.x);
            best_y = max(best_y, p.y + ratio * (q.y - p.y));
        }
        if ((p.y - 0.5L) * (q.y - 0.5L) < -1e-18L) {
            ld ratio = (0.5L - p.y) / (q.y - p.y);
            best_x = min(best_x, p.x + ratio * (q.x - p.x));
        }
    }

    cout << fixed << setprecision(9)
         << static_cast<double>(1.0L - best_y) << ' '
         << static_cast<double>(1.0L - best_x) << '\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