bzoj1043 下落的圆盘

1043: [HAOI2008]下落的圆盘

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1587  Solved: 669
[Submit][Status][Discuss]

Description

  有n个圆盘从天而降,后面落下的可以盖住前面的。求最后形成的封闭区域的周长。看下面这副图, 所有的红
色线条的总长度即为所求. 

Input

  第一行为1个整数n,N<=1000
接下来n行每行3个实数,ri,xi,yi,表示下落时第i个圆盘的半径和圆心坐标.

Output

  最后的周长,保留三位小数

Sample Input

2
1 0 0
1 1 0

Sample Output

10.472
分析:一开始的思路是每增加一个圆盘,看它能覆盖到被计入答案的区域,答案减掉这个长度.这个想法似乎是可行的,但是维护每个圆盘哪些位置被覆盖了非常困难.
          换个思路,考虑每个圆盘对答案的贡献:原周长-被后来的圆盘覆盖的周长.实际上后面一部分就是要求周长并,可以将圆拆成一条线段,规定一个起点和终点.但是这样的话还要求两个圆的交点,不好处理.一个比较好的方法是记录被覆盖部分的圆心角,放到[0,2π)这个区间里.利用两个圆心的极角和圆心角可以比较容易地计算.圆心角可以通过余弦定理得到.需要注意的是左右端点<0和≥2π的情况.
还要判断两圆是否是包含/相离关系.
          最后怎么求周长并?先把弧度并给求出来,乘上半径就是了.具体方法是在左端点+1,右端点-1,用cnt统计+1,-1的和,如果cnt > 0,则ans -= 第i个弧度-第i-1个弧度.ans初始化为2π.(有点复杂,结合代码更好理解)
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const double pi = acos(-1.0),eps = 1e-8;

int n,tot;
double ans,sum;

struct node
{
    double r,x,y;
} e[1010];

struct node2
{
    double x;
    int id;
} p[10010];

double D(node a,node b)
{
    return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y));
}

bool cmp(node2 a,node2 b)
{
    return a.x < b.x;
}

int main()
{
    scanf("%d",&n);
    for (int i = 1; i <= n; i++)
        scanf("%lf%lf%lf",&e[i].r,&e[i].x,&e[i].y);
    for (int i = 1; i <= n; i++)
    {
        bool flag = false;
        tot = 0;
        sum = 0.0;
        for (int j = i + 1; j <= n; j++)
        {
            double dist = D(e[i],e[j]);
            if (dist <= e[j].r - e[i].r)  //完全包含了
            {
                flag = 1;
                break;
            }
            if (dist > fabs(e[i].r - e[j].r) && dist <= e[i].r + e[j].r)  //必须要相交不能分离
            {
                double temp = acos((e[i].r * e[i].r + dist * dist - e[j].r * e[j].r) / (2 * e[i].r * dist));
                double temp2 = atan2(e[j].y - e[i].y,e[j].x - e[i].x);
                double l = temp2 - temp,r = temp2 + temp;
                if (l < 0 && r < 0)
                {
                    l += 2 * pi;
                    r += 2 * pi;
                }
                if (l >= 0 && r <= 2 * pi)
                {
                    p[++tot].x = l;
                    p[tot].id = 1;
                    p[++tot].x = r;
                    p[tot].id = -1;
                }
                else if (l < 0)
                {
                    p[++tot].x = l + 2 * pi;
                    p[tot].id = 1;
                    p[++tot].x = 2 * pi;
                    p[tot].id = -1;
                    p[++tot].x = 0;
                    p[tot].id = 1;
                    p[++tot].x = r;
                    p[tot].id = -1;
                }
                else
                {
                    p[++tot].x = l;
                    p[tot].id = 1;
                    p[++tot].x = 2 * pi;
                    p[tot].id = -1;
                    p[++tot].x = 0;
                    p[tot].id = 1;
                    p[++tot].x = r - 2 * pi;
                    p[tot].id = -1;
                }
            }
        }
        if (flag)
            continue;
        ans += e[i].r * 2 * pi;
        if (!tot)
            continue;
        sort(p + 1,p + 1 + tot,cmp);
        for(int j = 1; j <= tot; j++)
        {
            if (sum && fabs(p[j].x - p[j-1].x) > eps)
                ans -= e[i].r * (p[j].x - p[j - 1].x);
            sum += p[j].id;
        }
    }
    printf("%.3lf\n",ans);

    return 0;
}

 

posted @ 2017-12-26 20:50  zbtrs  阅读(197)  评论(0编辑  收藏  举报