欢迎光临
我们一直在努力

UVa 10969 Sweet Dream

题目描述

你站在房间中央,手里拿着一堆大小不一的圆盘。你可以随机选择一个圆盘并扔到地板上,重复此过程直到手上没有圆盘。圆盘落地后,有些圆盘会被后来的圆盘部分(或完全)覆盖。

你的任务是:从上方观察,计算所有圆盘可见部分的周长总和。

图 1 展示了圆盘相互覆盖的情况(此处省略图片,但可想象为若干圆形互相重叠,上层圆盘遮挡下层圆盘的部分区域)。

输入格式

第一行是一个整数 T≤100T \\le 100T100,表示测试用例的数量。

每个测试用例的第一行是一个整数 N≤100N \\le 100N100,表示圆盘的数量。

接下来 NNN 行,每行包含三个浮点数:rk≤100000r_k \\le 100000rk100000(半径),以及 −100000≤xk,yk≤100000-100000 \\le x_k, y_k \\le 100000100000xk,yk100000(圆心坐标)。

如果两个浮点数的绝对差小于 10−1010^{-10}1010,则认为它们相等。

输出格式

对于每个测试用例,输出一行一个浮点数,表示从上方可见的圆盘周长总和,四舍五入保留三位小数。

输出必须恰好有三位小数。

样例

输入

3
1
10 0 0
2
5 0 0
10 0 0
2
100 110

输出

62.832
62.832
10.472

题目分析

本题的核心是:给定若干个圆盘,按照输入顺序依次落到地面上(后输入的圆盘落在先输入的圆盘之上)。最终,从上往下看时,每个圆盘只能看到没有被任何后来的圆盘遮挡的部分。我们需要计算所有圆盘可见部分的总周长。

关键点分析

  • 遮挡关系:如果圆盘 iii 先落下,圆盘 jjj 后落下(j>ij > ij>i),则圆盘 jjj 可能遮挡圆盘 iii 的一部分。遮挡的区域是圆盘 iii 上的一段圆弧,该弧上的点同时位于圆盘 jjj 的内部。

  • 完全覆盖:

    • 如果后来的圆盘 jjj 完全包含圆盘 iii(即 d+ri≤rjd + r_i \\le r_jd+rirj),则圆盘 iii 的整个圆周都被遮挡,可见周长为 000
    • 如果后来的圆盘 jjj 完全被圆盘 iii 包含(即 d+rj≤rid + r_j \\le r_id+rjri),则圆盘 jjj 不会遮挡圆盘 iii 的任何部分(因为它在圆盘 iii 的内部,不会接触到圆盘 iii 的边界)。
  • 相交情况:若两圆相交(∣ri−rj∣<d<ri+rj|r_i – r_j| < d < r_i + r_jrirj<d<ri+rj),则圆盘 iii 上有一段连续的圆弧被圆盘 jjj 遮挡。该圆弧的圆心角(相对于圆心 OiO_iOiOjO_jOj 的方向)可以通过余弦定理求出。

  • 多个遮挡区间合并:一个圆盘可能被多个后来的圆盘遮挡,这些遮挡区间可能相互重叠。我们需要将这些区间合并,得到总遮挡弧度,然后用 2π2\\pi2π 减去它得到可见弧度,再乘以半径得到可见周长。

  • 解题思路

    1. 几何建模

    设圆盘 iii 的圆心为 OiO_iOi,半径为 RiR_iRi;圆盘 jjjj>ij > ij>i)的圆心为 OjO_jOj,半径为 rjr_jrj

    圆心距:d=∣OiOj∣d = |O_i O_j|d=OiOj

    如果两圆相交,则圆盘 iii 被圆盘 jjj 遮挡的弧段对应的半角 θ\\thetaθ 满足:

    cos⁡θ=Ri2+d2−rj22Rid
    \\cos\\theta = \\frac{R_i^2 + d^2 – r_j^2}{2 R_i d}
    cosθ=2RidRi2+d2rj2

    其中 θ∈[0,π]\\theta \\in [0, \\pi]θ[0,π]。这个公式来源于三角形 OiOjPO_i O_j POiOjPPPP 为两圆交点),由余弦定理得到。

    遮挡区间相对于向量 OiOj→\\overrightarrow{O_i O_j}OiOj 的方向角 α=atan2(dy,dx)\\alpha = \\text{atan2}(dy, dx)α=atan2(dy,dx) 对称分布,即区间为 [α−θ,α+θ][\\alpha – \\theta, \\alpha + \\theta][αθ,α+θ]

    2. 区间标准化与合并

    由于极角区间可能跨越 0002π2\\pi2π,我们需要将其标准化到 [0,2π)[0, 2\\pi)[0,2π)。具体做法是:

    • l<0l < 0l<0,拆分为 [l+2π,2π)[l + 2\\pi, 2\\pi)[l+2π,2π)[0,r][0, r][0,r]
    • r>2πr > 2\\pir>2π,拆分为 [l,2π)[l, 2\\pi)[l,2π)[0,r−2π][0, r – 2\\pi][0,r2π]

    然后对所有区间按左端点排序,依次合并重叠或相邻的区间,得到总覆盖弧度。

    3. 算法流程

    对于每个测试用例:

  • 读入所有圆盘信息。
  • 初始化答案变量 ans=0\\textit{ans} = 0ans=0
  • 对于每个圆盘 iii0≤i<N0 \\le i < N0i<N):
    • 创建一个空的区间列表 blocked\\textit{blocked}blocked,用于存储所有被遮挡的弧段。
    • 遍历所有后来的圆盘 jjjj>ij > ij>i):
      • 计算圆心距 ddd
      • 如果 d+Ri≤rj+epsd + R_i \\le r_j + \\text{eps}d+Rirj+eps:圆盘 jjj 完全包含圆盘 iii,则圆盘 iii 完全不可见,将 blocked\\textit{blocked}blocked 设为 [0,2π)[0, 2\\pi)[0,2π) 并跳出循环。
      • 如果 d+rj≤Ri+epsd + r_j \\le R_i + \\text{eps}d+rjRi+epsd≥Ri+rj−epsd \\ge R_i + r_j – \\text{eps}dRi+rjeps:无遮挡,跳过。
      • 否则,计算方向角 α\\alphaα 和半角 θ\\thetaθ,得到区间 [α−θ,α+θ][\\alpha – \\theta, \\alpha + \\theta][αθ,α+θ],标准化并加入 blocked\\textit{blocked}blocked
    • 合并 blocked\\textit{blocked}blocked 中的所有区间,得到总遮挡弧度 covered\\textit{covered}covered
    • 可见周长 =(2π−covered)×Ri= (2\\pi – \\textit{covered}) \\times R_i=(2πcovered)×Ri,累加到 ans\\textit{ans}ans
  • 输出 ans\\textit{ans}ans,保留三位小数。
  • 4. 复杂度分析

    • 每个测试用例有 NNN 个圆盘,对每个圆盘 iii 需要检查 N−iN – iNi 个后来的圆盘。
    • 每次检查 O(1)O(1)O(1) 时间。
    • 区间合并:最多 O(N)O(N)O(N) 个区间,排序 O(Nlog⁡N)O(N \\log N)O(NlogN)
    • 总时间复杂度:O(N2+N2log⁡N)=O(N2log⁡N)O(N^2 + N^2 \\log N) = O(N^2 \\log N)O(N2+N2logN)=O(N2logN),在 N≤100N \\le 100N100 时完全可行。

    代码实现

    // Sweet Dream
    // UVa ID: 10969
    // Verdict: Accepted
    // Submission Date: 2026-06-11
    // UVa Run Time: 0.000s
    //
    // 版权所有(C)2026,邱秋。metaphysis # yeah dot net

    #include <bits/stdc++.h>
    using namespace std;

    const double PI = acos(1.0);
    const double EPS = 1e-10;

    struct Circle { double x, y, r; };

    double normAngle(double a) {
    while (a < 0) a += 2 * PI;
    while (a >= 2 * PI) a -= 2 * PI;
    return a;
    }

    vector<pair<double, double>> addInterval(vector<pair<double, double>> &intervals, double l, double r) {
    if (r l > 2 * PI + EPS) {
    intervals.emplace_back(0, 2 * PI);
    return intervals;
    }
    l = normAngle(l);
    r = normAngle(r);
    if (l < r) intervals.emplace_back(l, r);
    else {
    intervals.emplace_back(l, 2 * PI);
    intervals.emplace_back(0, r);
    }
    return intervals;
    }

    double mergeAndGetCovered(vector<pair<double, double>> &intervals) {
    if (intervals.empty()) return 0.0;
    sort(intervals.begin(), intervals.end());
    double covered = 0.0;
    double curL = intervals[0].first, curR = intervals[0].second;
    for (size_t i = 1; i < intervals.size(); i++) {
    if (intervals[i].first <= curR + EPS) {
    curR = max(curR, intervals[i].second);
    } else {
    covered += curR curL;
    curL = intervals[i].first;
    curR = intervals[i].second;
    }
    }
    covered += curR curL;
    return covered;
    }

    int main() {
    int T;
    scanf("%d", &T);
    while (T) {
    int N;
    scanf("%d", &N);
    vector<Circle> c(N);
    for (int i = 0; i < N; i++) {
    scanf("%lf%lf%lf", &c[i].r, &c[i].x, &c[i].y);
    }

    double ans = 0.0;

    for (int i = 0; i < N; i++) {
    vector<pair<double, double>> blocked;

    for (int j = i + 1; j < N; j++) {
    double dx = c[j].x c[i].x, dy = c[j].y c[i].y;
    double d = sqrt(dx * dx + dy * dy);
    double R = c[i].r, r = c[j].r;

    if (d + R <= r + EPS) {
    blocked.clear();
    blocked.emplace_back(0, 2 * PI);
    break;
    }
    if (d + r <= R + EPS || d >= R + r EPS)
    continue;

    double ang = atan2(dy, dx);
    double theta = acos((R * R + d * d r * r) / (2 * R * d));
    addInterval(blocked, ang theta, ang + theta);
    }

    double covered = mergeAndGetCovered(blocked);
    ans += (2 * PI covered) * c[i].r;
    }

    printf("%.3lf\\n", ans);
    }
    return 0;
    }

    总结

    本题是一个计算几何与区间合并相结合的典型问题。关键点包括:

  • 遮挡关系的几何建模:利用余弦定理求出遮挡圆弧的圆心角范围。
  • 区间标准化与合并:处理极角区间在 [0,2π)[0, 2\\pi)[0,2π) 上可能出现的跨越边界的情况。
  • 完全覆盖的处理:一旦某个后来的圆盘完全包含当前圆盘,当前圆盘即完全不可见,无需再检查其他圆盘。
  • 浮点数精度控制:使用 eps=10−10\\text{eps} = 10^{-10}eps=1010 进行容差比较,避免因浮点误差导致的错误判断。
  • 技巧总结:

    • 将几何问题转化为区间覆盖问题,是处理圆形遮挡问题的常用方法。
    • 对于极角区间,标准化到 [0,2π)[0, 2\\pi)[0,2π) 后,跨越 2π2\\pi2π 的情况可以通过拆分区间来处理。
    • 按照输入顺序(即掉落顺序)处理遮挡关系,可以自然地保证后落的圆盘覆盖先落的圆盘。

    本题虽小,但涵盖了计算几何、区间算法和精度控制等多个知识点,是一道很好的综合练习题。

    赞(0)
    未经允许不得转载:171主机测评 » UVa 10969 Sweet Dream
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址