Challenge: Painting Figures

Scor cont: 100.0 / 100

Submission status: Accepted

Submission score: 1.0

Submission ID: 464915986

Limbaj: cpp14

Link challenge: https://www.hackerrank.com/challenges/painting-figures/problem

Cerinta

Carl is an abstract artist painting $n$ figures. Each figure $i$ is described as a segment on a plane with ends in $(x_{1}, y_{1})$ and $(x_{2}, y_{2})$ having radius $r$; this means every point with a distance $\le r$ from the segment is part of figure $i$. Carl wants to make sure he has enough paint for all the figures, so he wants to know the *total area* they will cover.

Given the locations for all the figures, find and print a real number denoting the total area covered by all $n$ figures with an absolute or relative error of *at most* $10^{-6}$.

Input Format

The first line contains single integer, $n$, denoting the number of figures.		
Each line $i$ of the $n$ subsequent lines contains five space-separated integers describing the respective values of $x_{1}$, $y_{1}$, $x_{2}$, $y_{2}$, and $r$ for figure $i$.

Output Format

Print a real number denoting the total area covered by all $n$ figures with an absolute or relative error of *at most* $10^{-6}$.

Constraints

+ $1 \le n \le 200$
- $0 \le |x_{1}|, |y_{1}|, |x_{2}|, |y_{2}|, r \le 10^3$. 
- It's guaranteed that each segment's length and $r$ values are positive.

Cod sursa

#pragma GCC optimize("Ofast,unroll-loops")
#include <bits/stdc++.h>
using namespace std;

struct Fig {
    double x1, y1, x2, y2;
    double dx, dy;
    double r, r2;
    double len2, len, rlen;
};

struct Node {
    double a, b;
    double fa, fb, fm;
    double whole;
    double eps;
    int depth;
};

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

    int n;
    if (!(cin >> n)) return 0;
    if (n <= 0) {
        cout << fixed << setprecision(10) << 0.0 << '\n';
        return 0;
    }

    vector<Fig> figs;
    figs.reserve(n);
    vector<double> cuts;
    cuts.reserve(4 * n + 4);

    double y_lo = 1e100, y_hi = -1e100;
    for (int i = 0; i < n; ++i) {
        Fig f{};
        cin >> f.x1 >> f.y1 >> f.x2 >> f.y2 >> f.r;
        f.dx = f.x2 - f.x1;
        f.dy = f.y2 - f.y1;
        f.r2 = f.r * f.r;
        f.len2 = f.dx * f.dx + f.dy * f.dy;
        f.len = sqrt(f.len2);
        f.rlen = f.r * f.len;
        figs.push_back(f);

        double lo = min(f.y1, f.y2) - f.r;
        double hi = max(f.y1, f.y2) + f.r;
        y_lo = min(y_lo, lo);
        y_hi = max(y_hi, hi);
        cuts.push_back(f.y1 - f.r);
        cuts.push_back(f.y1 + f.r);
        cuts.push_back(f.y2 - f.r);
        cuts.push_back(f.y2 + f.r);
    }

    const double EPS = 1e-12;

    auto union_length_at = [&](double y) -> double {
        pair<double, double> ivs[205];
        int iv_cnt = 0;

        for (const auto& f : figs) {
            double xl = 1e100, xr = -1e100;

            // Endpoint circles
            double d = y - f.y1;
            double d2 = d * d;
            if (d2 <= f.r2 + EPS) {
                double hw = sqrt(max(0.0, f.r2 - d2));
                xl = min(xl, f.x1 - hw);
                xr = max(xr, f.x1 + hw);
            }
            d = y - f.y2;
            d2 = d * d;
            if (d2 <= f.r2 + EPS) {
                double hw = sqrt(max(0.0, f.r2 - d2));
                xl = min(xl, f.x2 - hw);
                xr = max(xr, f.x2 + hw);
            }

            // Segment body: intersection of strip around infinite line and projection slab.
            bool has = true;
            double lo = -1e100, hi = 1e100;

            if (fabs(f.dy) > EPS) {
                // |dy*x + (dx*y1 - dy*x1 - dx*y)| <= r*|D|
                double c0 = f.dx * f.y1 - f.dy * f.x1 - f.dx * y;
                lo = (-f.rlen - c0) / f.dy;
                hi = ( f.rlen - c0) / f.dy;
                if (lo > hi) swap(lo, hi);
            } else {
                // Horizontal segment: strip active only if |y-y1|<=r.
                if (fabs(y - f.y1) > f.r + EPS) {
                    has = false;
                }
            }

            if (has) {
                if (fabs(f.dx) > EPS) {
                    // 0 <= dx*x + (-dx*x1 + dy*(y-y1)) <= |D|^2
                    double p0 = -f.dx * f.x1 + f.dy * (y - f.y1);
                    double a = (-p0) / f.dx;
                    double b = (f.len2 - p0) / f.dx;
                    if (a > b) swap(a, b);
                    if (a > lo) lo = a;
                    if (b < hi) hi = b;
                } else {
                    // Vertical segment: projection independent of x.
                    double P = f.dy * (y - f.y1);
                    if (P < -EPS || P > f.len2 + EPS) has = false;
                }
            }

            if (has && lo <= hi + EPS) {
                if (lo < xl) xl = lo;
                if (hi > xr) xr = hi;
            }

            if (xl <= xr + EPS) ivs[iv_cnt++] = {xl, xr};
        }

        if (iv_cnt == 0) return 0.0;

        sort(ivs, ivs + iv_cnt);
        double total = 0.0;
        double ca = ivs[0].first, cb = ivs[0].second;
        for (int i = 1; i < iv_cnt; ++i) {
            double a = ivs[i].first, b = ivs[i].second;
            if (a <= cb + EPS) {
                if (b > cb) cb = b;
            } else {
                total += cb - ca;
                ca = a;
                cb = b;
            }
        }
        total += cb - ca;
        return total;
    };

    auto integrate_adaptive = [&](double a, double b, double tol) -> double {
        double fa = union_length_at(a);
        double fb = union_length_at(b);
        double m = (a + b) * 0.5;
        double fm = union_length_at(m);
        double whole = (b - a) / 6.0 * (fa + 4.0 * fm + fb);

        vector<Node> st;
        st.reserve(1 << 15);
        st.push_back({a, b, fa, fb, fm, whole, tol, 0});

        double result = 0.0;
        while (!st.empty()) {
            Node cur = st.back();
            st.pop_back();

            double l = cur.a, r = cur.b;
            double mid = (l + r) * 0.5;
            double m1 = (l + mid) * 0.5;
            double m2 = (mid + r) * 0.5;

            double fm1 = union_length_at(m1);
            double fm2 = union_length_at(m2);

            double h = r - l;
            double left = h / 12.0 * (cur.fa + 4.0 * fm1 + cur.fm);
            double right = h / 12.0 * (cur.fm + 4.0 * fm2 + cur.fb);
            double total = left + right;
            double diff = total - cur.whole;

            if (cur.depth >= 50 || (cur.depth >= 8 && fabs(diff) <= 15.0 * cur.eps)) {
                result += total + diff / 15.0;
            } else {
                double he = cur.eps * 0.5;
                st.push_back({mid, r, cur.fm, cur.fb, fm2, right, he, cur.depth + 1});
                st.push_back({l, mid, cur.fa, cur.fm, fm1, left, he, cur.depth + 1});
            }
        }

        return result;
    };

    cuts.push_back(y_lo);
    cuts.push_back(y_hi);
    sort(cuts.begin(), cuts.end());
    vector<double> uniq;
    uniq.reserve(cuts.size());
    for (double v : cuts) {
        if (uniq.empty() || fabs(v - uniq.back()) > 1e-12) uniq.push_back(v);
    }

    // Dynamic global tolerance from coarse area estimate.
    double area_est = 0.0;
    {
        const int M_EST = 96;
        double L = y_hi - y_lo;
        if (L > 0.0) {
            double h = L / M_EST;
            double prev = union_length_at(y_lo);
            for (int i = 1; i <= M_EST; ++i) {
                double y = y_lo + h * i;
                double cur = union_length_at(y);
                area_est += (prev + cur) * 0.5 * h;
                prev = cur;
            }
        }
    }

    struct Seg { double a, b, eps; };
    vector<Seg> segs;
    segs.reserve(uniq.size());

    const double total_tol = 2e-8 * max(1.0, fabs(area_est));
    const double total_len = max(1e-12, y_hi - y_lo);
    for (size_t i = 1; i < uniq.size(); ++i) {
        double a = uniq[i - 1], b = uniq[i];
        if (b - a <= 1e-12) continue;
        double seg_tol = total_tol * (b - a) / total_len;
        if (seg_tol < 1e-12) seg_tol = 1e-12;
        segs.push_back({a, b, seg_tol});
    }

    double ans = 0.0;
    unsigned hc = thread::hardware_concurrency();
    int tcnt = (hc == 0 ? 1 : (int)hc);
    if (tcnt > 4) tcnt = 4;

    if (tcnt <= 1 || segs.size() < 24) {
        for (const auto& s : segs) ans += integrate_adaptive(s.a, s.b, s.eps);
    } else {
        vector<double> partial(tcnt, 0.0);
        vector<thread> th;
        th.reserve(tcnt);
        for (int t = 0; t < tcnt; ++t) {
            th.emplace_back([&, t]() {
                double loc = 0.0;
                for (size_t j = (size_t)t; j < segs.size(); j += (size_t)tcnt) {
                    const auto& s = segs[j];
                    loc += integrate_adaptive(s.a, s.b, s.eps);
                }
                partial[t] = loc;
            });
        }
        for (auto& thd : th) thd.join();
        for (double v : partial) ans += v;
    }

    cout << fixed << setprecision(10) << ans << '\n';
    return 0;
}
HackerRank Geometry – Painting Figures