ICPC 2023
ICPC 2023

D. Carl’s Vacation

47th Annual hosted by ICPC World Championship AASTMT Problem D Carl’s Vacation T...

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 D
                                            Carl’s Vacation
                                          Time limit: 1 second
Carl the ant is back! After traversing meandering paths (Problem A, 2004 World Finals) and wandering
over octahedrons (Problem C, 2009 World Finals) it is time for a little vacation — time to see the sights!
And where better to see the sights than at the tips of tall structures like, say, pyramids!! And where
better to see tall pyramids but Egypt!!! (This is so exciting!!!!!)
After taking in the view from the tip of one pyramid, Carl would like to go to the tip of another. Since
ants do not do particularly well in the hot sun, he wants to find the minimum distance to travel between
the tips of these two pyramids, assuming he can only walk on the surfaces of the pyramids and the plane
which the pyramids sit upon. The pyramids are, geometrically, right square pyramids, meaning the apex
of the pyramid lies directly above the center of a square base.
50
40
30
20
10
0
-10       0         10       20        30        40
Figure D.1: Illustration of two pyramids corresponding to Sample Input 1. The black line
shows the shortest path between their apexes.

Input

The first line of input contains five integers x1 , y1 , x2 , y2 , h where x1 , y1 , x2 , y2 (−105 ≤ x1 , x2 , y1 , y2 ≤ 105 and (x1 , y1 ) 6= (x2 , y2 )) define an edge of the first pyramid, with the body of the pyramid lying to the left of the directed vector from (x1 , y1 ) to (x2 , y2 ), and h (1 ≤ h ≤ 105 ) is the height of the pyramid. The second line of input describes the second pyramid in the same format. The intersection of the bases of the two pyramids has 0 area.

Output

Output the minimum distance Carl travels between the tips of the two pyramids. 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
0 0 10 0 4
9 18 34 26 42
Sample Output
60.866649532

Editorial

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

Key Observations

  • Once we choose one side face on each pyramid, the path has three pieces: a segment on the first triangular face from the first apex to some point on that face's base edge, a straight ground segment, and a segment on the second triangular face to the second apex.

  • There are only $4 \cdot 4 = 16$ choices of side faces.

  • If $p$ and $q$ are the two ground contact points, then the total length is \[ \sqrt{\|p-c_1\|^2+h_1^2}+\|p-q\|+\sqrt{\|q-c_2\|^2+h_2^2}, \] where $c_i$ is the center of the $i$th base and $h_i$ its height.

  • Parameterizing $p$ and $q$ along their chosen edges gives a convex function on $[0,1]\times[0,1]$, so nested ternary search finds the minimum for that edge pair.

Algorithm

  1. Reconstruct the four base corners of each pyramid from the given directed edge. The remaining two corners are obtained by moving along a left normal of the edge.

  2. For every edge $e$ of the first base and every edge $f$ of the second base:

    • let $p(x)$ be the point at parameter $x \in [0,1]$ on $e$;

    • let $q(y)$ be the point at parameter $y \in [0,1]$ on $f$;

    • define \[ L(x,y)=\sqrt{\|p(x)-c_1\|^2+h_1^2}+\|p(x)-q(y)\|+\sqrt{\|q(y)-c_2\|^2+h_2^2}. \]

    • For fixed $x$, minimize $L(x,y)$ over $y$ by ternary search.

    • Minimize the resulting one-variable function over $x$ by another ternary search.

    • Take the minimum over all $16$ edge pairs. enumerate

      Correctness Proof

      We prove that the algorithm returns the correct answer.

      Lemma 1.

      For a fixed triangular side face and a fixed point on its base edge, the shortest path from the apex to that point is the straight segment inside the triangle.

      Proof.

      The face is a planar triangle, hence convex. In a plane, the shortest path between two points is their Euclidean segment, and that segment stays inside the triangle. □

      Lemma 2.

      For some optimal route, the path uses exactly one side face of each pyramid and one ground segment, as enumerated by the algorithm.

      Proof.

      Any valid route must leave each pyramid through some point of its square base boundary and then travel across the ground plane. Fix those two boundary points. By Lemma 1, the best way to connect an apex to its chosen boundary point is a straight segment within one incident triangular face. The best ground-plane path between the two boundary points is the straight segment joining them. Therefore some optimal route has exactly the form considered by the algorithm. □

      Lemma 3.

      For a fixed pair of base edges, the function $L(x,y)$ is convex on $[0,1]\times[0,1]$.

      Proof.

      The maps $x \mapsto p(x)$ and $y \mapsto q(y)$ are affine. The Euclidean norm is convex, so each term \[ \sqrt{\|p(x)-c_1\|^2+h_1^2},\quad \|p(x)-q(y)\|,\quad \sqrt{\|q(y)-c_2\|^2+h_2^2} \] is convex in its arguments, and their sum is convex as well. Hence every one-dimensional restriction used by the nested ternary searches is unimodal. □

      Theorem.

      The algorithm outputs the minimum possible travel distance.

      Proof.

      By Lemma 2, some optimal route is represented by one of the $16$ edge pairs checked by the algorithm. For that pair, Lemma 3 implies that nested ternary search finds the global minimum of $L(x,y)$. Taking the minimum over all edge pairs therefore yields the true optimum. □

      Complexity Analysis

      The algorithm checks $16$ edge pairs, and for each it performs a constant number of ternary-search iterations. Therefore the running time is $O(1)$ and the memory usage is $O(1)$.

      Implementation Notes

      • The implementation performs $80$ iterations in each ternary search, which is ample for the required error bound.

      • All geometry is done in long double.

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;

    Point operator+(const Point& other) const {
        return {x + other.x, y + other.y};
    }
    Point operator-(const Point& other) const {
        return {x - other.x, y - other.y};
    }
    Point operator*(ld scale) const {
        return {x * scale, y * scale};
    }
};

ld dot(const Point& a, const Point& b) {
    return a.x * b.x + a.y * b.y;
}

ld norm(const Point& a) {
    return sqrtl(dot(a, a));
}

Point left_normal(const Point& v) {
    return {-v.y, v.x};
}

struct Pyramid {
    array<Point, 4> base;
    Point center;
    ld height = 0;
};

Pyramid read_pyramid() {
    long long x1, y1, x2, y2, h;
    cin >> x1 >> y1 >> x2 >> y2 >> h;

    Point a{static_cast<ld>(x1), static_cast<ld>(y1)};
    Point b{static_cast<ld>(x2), static_cast<ld>(y2)};
    Point edge = b - a;
    ld side = norm(edge);
    Point inside = left_normal(edge) * (1.0L / side);

    Pyramid pyramid;
    pyramid.base[0] = a;
    pyramid.base[1] = b;
    pyramid.base[2] = b + inside * side;
    pyramid.base[3] = a + inside * side;
    pyramid.center = (pyramid.base[0] + pyramid.base[2]) * 0.5L;
    pyramid.height = h;
    return pyramid;
}

void solve() {
    Pyramid first = read_pyramid();
    Pyramid second = read_pyramid();

    auto point_on_edge = [](const Point& a, const Point& b, ld t) {
        return a + (b - a) * t;
    };

    auto distance_to_apex = [](const Point& center, ld height, const Point& p) {
        Point diff = p - center;
        return sqrtl(dot(diff, diff) + height * height);
    };

    ld answer = 1e100L;
    for (int i = 0; i < 4; ++i) {
        Point e1 = first.base[i];
        Point e2 = first.base[(i + 1) % 4];
        for (int j = 0; j < 4; ++j) {
            Point f1 = second.base[j];
            Point f2 = second.base[(j + 1) % 4];

            auto value = [&](ld x, ld y) {
                Point p = point_on_edge(e1, e2, x);
                Point q = point_on_edge(f1, f2, y);
                return distance_to_apex(first.center, first.height, p) +
                       norm(p - q) +
                       distance_to_apex(second.center, second.height, q);
            };

            auto best_for_x = [&](ld x) {
                ld low = 0;
                ld high = 1;
                for (int it = 0; it < 80; ++it) {
                    ld m1 = (2 * low + high) / 3;
                    ld m2 = (low + 2 * high) / 3;
                    if (value(x, m1) < value(x, m2)) {
                        high = m2;
                    } else {
                        low = m1;
                    }
                }
                return value(x, (low + high) * 0.5L);
            };

            ld low = 0;
            ld high = 1;
            for (int it = 0; it < 80; ++it) {
                ld m1 = (2 * low + high) / 3;
                ld m2 = (low + 2 * high) / 3;
                if (best_for_x(m1) < best_for_x(m2)) {
                    high = m2;
                } else {
                    low = m1;
                }
            }
            answer = min(answer, best_for_x((low + high) * 0.5L));
        }
    }

    cout << fixed << setprecision(9) << static_cast<double>(answer) << '\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