ICPC 2020
ICPC 2020

C. Domes

Saint Basil’s Cathedral is the best-known landmark of Moscow and maybe even of all of Russia. Built under Ivan the Terrible in the 16th century, the cathedral is known for its colorful domes. No visit to the city is complete without taking a photo of the fo...

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

Problem Statement

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

Time limit 2 seconds

Saint Basil’s Cathedral is the best-known landmark of Moscow and maybe even of all of Russia. Built under Ivan the Terrible in the 16th century, the cathedral is known for its colorful domes. No visit to the city is complete without taking a photo of the former church in Red Square.

Figure C.1: Two views of Saint Basil’s Cathedral.
The Moscow Tourism Board (MTB) wants to make it as safe as possible for tourists to take the perfect
shot of the cathedral. Depending on where you stand when you take a picture, the relative positions of the
domes will be different (see Figure C.1). The MTB is concerned that for some desired configurations of
domes the region in Red Square where such a photo is possible will be so small as to lead to a dangerous
overcrowding of photographers. Wanting to avoid the inevitable pushing, shoving, injury, and Covid
that this could cause, the MTB would like to find the area of the region where a photo is possible for any
desired ordering of the domes.
For simplicity, assume that cameras have a 180-degree viewing angle. As
an illustration consider Figure C.2, which shows the location of the domes
(labeled 1–5) and the photographer (green dot) in the plane. If the pho-                  1
                                                                                              2
tographer shoots a picture aiming the camera straight towards the left (di-           5
rectly at dome 5), then everything in the shaded area will be visible in the
photograph. Note that in this photograph, the domes will appear in order                      3
                                                                                          4
4, 3, 5, 2, 1 from the left to the right.
Given the location of the domes within Red Square and a desired left-to-
right order of the domes in the photograph, MTB wants to know the area of Figure C.2: Illustration of
the region within Red Square from which such a photograph can be taken. the sample inputs.
You can assume that the domes are points, so that they do not block each
other unless they are in a straight line from the photographer’s view.

Input

The first line of input contains three integers dx , dy , and n, where dx and dy (2 ≤ dx , dy ≤ 105 ) are the dimensions of Red Square, and n (1 ≤ n ≤ 100) is the number of domes. The bottom-left corner of Red Square is at the origin (0, 0) and the top-right corner is at coordinate (dx , dy ). Each of the next n lines contains two integers xi and yi (0 < xi < dx , 0 < yi < dy ), giving the locations (xi , yi ) of the domes. The ith line describes dome number i. No two domes are in the same location. The last line contains a permutation of the numbers {1, . . . , n} specifying the desired left-to-right view- ing order of the domes in the picture.

Output

Output the area of the region within Red Square from which one can take a photo that shows the domes in the requested order. Note that the area may be 0 if there is no position from which to take the requested photo. Your answer should have an absolute or relative error of at most 10−3 .

Sample Tests

Sample 1
Sample Input
 100 100 5
 30 70
 50 60
 50 40
 30 30
 20 50
 4 3 5 2 1
Sample Output
450.000000
Sample 2
Sample Input
 100 100 5
 30 70
 50 60
 50 40
 30 30
 20 50
 1 2 5 4 3
Sample Output
0.000000

Editorial

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

Key Observations

  • In a 180-degree photo, the domes are ordered exactly by the angular order of the rays from the photographer. Therefore for every pair $i < j$, dome $p_i$ must appear before dome $p_j$ in that angular order.

  • For a fixed pair $i < j$, the condition ``$p_i$ is before $p_j$'' is equivalent to saying that the photographer lies on the right-hand side of the directed line from dome $p_i$ to dome $p_j$. In coordinates, if $A$ is the position of $p_i$, $B$ the position of $p_j$, and $X$ the photographer, then we need \[ \operatorname{cross}(B-A,\; X-A) \le 0. \]

  • Hence the feasible region is the intersection of:

    • the original rectangle,

    • one half-plane for every pair $i < j$.

    • An intersection of half-planes is convex, so we can start from the rectangle and clip it repeatedly. Since $n \le 100$, there are at most $\binom{100}{2} = 4950$ half-planes, which is small enough for ordinary polygon clipping.

    Algorithm

    1. Reorder the dome coordinates according to the requested permutation $p_1, p_2, \dots, p_n$.

    2. Initialize the current polygon as the rectangle \[ (0,0),\ (dx,0),\ (dx,dy),\ (0,dy). \]

    3. For every pair $i < j$:

      • let $A$ be dome $p_i$ and $B$ be dome $p_j$;

      • clip the current polygon with the half-plane \[ \operatorname{cross}(B-A,\; X-A) \le 0. \]

      • We use the standard Sutherland--Hodgman clipping step for one convex polygon against one line.

      • After all clips, compute the area of the remaining polygon with the shoelace formula. If the polygon becomes empty, the answer is $0$. enumerate

        Correctness Proof

        We prove that the algorithm returns the correct answer.

        Lemma 1.

        For two domes $A$ and $B$, with $A$ required to appear before $B$ in the photo, a photographer position $X$ is valid for this pair if and only if $X$ lies on the right-hand side of the directed line $A \to B$.

        Proof.

        The left-to-right order in a 180-degree photo is the angular order of the rays from $X$ to the domes inside one visible semicircle. So ``$A$ appears before $B$'' means that when rotating from ray $XA$ to ray $XB$, we move clockwise by an angle strictly less than $\pi$. That is exactly the condition that the oriented triangle $(A,B,X)$ has non-positive signed area, or equivalently \[ \operatorname{cross}(B-A,\; X-A) \le 0. \] The boundary case is collinearity; it has area $0$, so including it does not affect the final answer. □

        Lemma 2.

        If a position $X$ satisfies the half-plane condition for every pair $i < j$, then there exists a 180-degree photo from which the domes appear in the requested order $p_1, p_2, \dots, p_n$.

        Proof.

        Let $\alpha_k$ be the polar angle of the ray from $X$ to dome $p_k$. For every $i < j$, Lemma 1 gives that the directed angle from ray $Xp_i$ to ray $Xp_j$ is clockwise and smaller than $\pi$. Therefore the angles can be lifted to real numbers so that \[ \alpha_1 > \alpha_2 > \dots > \alpha_n \quad\text{and}\quad \alpha_1 - \alpha_n < \pi. \] So all rays lie inside one open semicircle, and their angular order inside that semicircle is exactly $p_1, p_2, \dots, p_n$. Point the camera toward the middle direction of that semicircle. Then all domes are visible and appear in the required left-to-right order. □

        Lemma 3.

        If there exists a valid photo from position $X$ whose dome order is $p_1, p_2, \dots, p_n$, then $X$ satisfies all the half-plane constraints used by the algorithm.

        Proof.

        In a valid photo, for every pair $i < j$, dome $p_i$ appears before dome $p_j$. By Lemma 1, this implies that $X$ lies on the right-hand side of the directed line from $p_i$ to $p_j$. Hence all pair constraints hold. □

        Theorem.

        The algorithm outputs the correct answer for every valid input.

        Proof.

        By Lemma 3, every valid photographer position lies in every half-plane used by the algorithm, so it survives all clipping steps. By Lemma 2, every position that survives all clipping steps is indeed valid for the requested photo order. Thus the final polygon produced by the algorithm is exactly the feasible region inside the rectangle. The shoelace formula therefore computes exactly the desired area. □

        Complexity Analysis

        There are $O(n^2)$ half-planes. Each clipping step is linear in the current polygon size, which is also $O(n^2)$ in the worst case. Therefore the running time is $O(n^4)$ in the worst case, which is easily fast enough for $n \le 100$.

        The polygon itself has $O(n^2)$ vertices, so the memory usage is $O(n^2)$.

        Implementation Notes

        • The algorithm clips with all pairs $i < j$, not only consecutive pairs in the permutation.

        • Using long double for the geometric computation is sufficient for the required $10^{-3}$ accuracy.

        • After clipping, removing consecutive duplicate vertices helps keep the polygon stable numerically.

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 {

const long double EPS = 1e-12L;

struct Point {
    long double x = 0;
    long double y = 0;
};

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

Point intersection(const Point& p, const Point& q, const Point& a, const Point& b) {
    long double d1 = cross(a, b, p);
    long double d2 = cross(a, b, q);
    long double t = d1 / (d1 - d2);
    Point result;
    result.x = p.x + (q.x - p.x) * t;
    result.y = p.y + (q.y - p.y) * t;
    return result;
}

bool inside(const Point& p, const Point& a, const Point& b) {
    return cross(a, b, p) <= EPS;
}

bool same_point(const Point& a, const Point& b) {
    return fabsl(a.x - b.x) <= 1e-10L && fabsl(a.y - b.y) <= 1e-10L;
}

vector<Point> clip_polygon(const vector<Point>& polygon, const Point& a, const Point& b) {
    if (polygon.empty()) {
        return {};
    }

    vector<Point> result;
    int m = int(polygon.size());
    for (int i = 0; i < m; ++i) {
        Point current = polygon[i];
        Point next = polygon[(i + 1) % m];
        bool current_inside = inside(current, a, b);
        bool next_inside = inside(next, a, b);

        if (current_inside && next_inside) {
            result.push_back(next);
        } else if (current_inside && !next_inside) {
            result.push_back(intersection(current, next, a, b));
        } else if (!current_inside && next_inside) {
            result.push_back(intersection(current, next, a, b));
            result.push_back(next);
        }
    }

    vector<Point> cleaned;
    for (const Point& p : result) {
        if (cleaned.empty() || !same_point(cleaned.back(), p)) {
            cleaned.push_back(p);
        }
    }
    if (!cleaned.empty() && same_point(cleaned.front(), cleaned.back())) {
        cleaned.pop_back();
    }
    return cleaned;
}

long double polygon_area(const vector<Point>& polygon) {
    if (polygon.size() < 3) {
        return 0;
    }

    long double twice_area = 0;
    int m = int(polygon.size());
    for (int i = 0; i < m; ++i) {
        const Point& current = polygon[i];
        const Point& next = polygon[(i + 1) % m];
        twice_area += current.x * next.y - current.y * next.x;
    }
    return fabsl(twice_area) / 2;
}

void solve() {
    int dx, dy, n;
    cin >> dx >> dy >> n;

    vector<Point> domes(n);
    for (int i = 0; i < n; ++i) {
        cin >> domes[i].x >> domes[i].y;
    }

    vector<int> order(n);
    for (int i = 0; i < n; ++i) {
        cin >> order[i];
        --order[i];
    }

    vector<Point> polygon;
    polygon.push_back({0, 0});
    polygon.push_back({(long double)dx, 0});
    polygon.push_back({(long double)dx, (long double)dy});
    polygon.push_back({0, (long double)dy});

    for (int i = 0; i < n && !polygon.empty(); ++i) {
        for (int j = i + 1; j < n && !polygon.empty(); ++j) {
            polygon = clip_polygon(polygon, domes[order[i]], domes[order[j]]);
        }
    }

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