列表

详情


NC19969. [HAOI2008]下落的圆盘

描述

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

输入描述

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

输出描述

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

示例1

输入:

2
1 0 0
1 1 0

输出:

10.472

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++14(g++5.4) 解法, 执行用时: 34ms, 内存消耗: 608K, 提交时间: 2020-04-11 23:34:09

//#include<bits/stdc++.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const double eps = 1e-10;
const int MAXN=1005;
//const double PI=acos(-1);
double pi=acos(-1.0);
int sgn(double d)
{
    return d<-eps?-1:d>eps;
}
int dcmp(double x) //减少精度问题
{
    if(fabs(x) < eps)
        return 0;
    if(x < 0)
        return -1;
    return 1;
}
struct Point //点定义
{
    double x,y;
    Point(double x=0,double y=0):x(x),y(y) {}
};
typedef Point Vector;

Vector operator+(Vector A,Vector B)
{
    return Vector(A.x+B.x,A.y+B.y);
}
Vector operator-(Vector A,Vector B)
{
    return Vector(A.x-B.x,A.y-B.y);
}
Vector operator*(Vector A,double p)
{
    return Vector(A.x*p,A.y*p);
}
Vector operator/(Vector A,double p)
{
    return Vector(A.x/p,A.y/p);
}

bool operator < (const Point& a, const Point& b)
{
    if(a.x == b.x)
        return a.y < b.y;
    return a.x < b.x;
}
bool operator == (const Point& a, const Point& b)
{
    if(dcmp(a.x-b.x) == 0 && dcmp(a.y-b.y) == 0)
        return true;
    return false;
}

double Dot(Vector A, Vector B) //向量内积,求的是夹角为锐角还是钝角,点乘
{
    return A.x*B.x + A.y*B.y;
}

double Cross(Vector A, Vector B) //向量外积,判断A和B哪个在前(>0,B在A逆时针方向),A、B是否共线等。
{
    return A.x*B.y-A.y*B.x;
}
double Length(Vector A) //长度
{
    return sqrt(Dot(A, A));
}

double Angle(Vector A, Vector B) //返回值为弧度制下的夹角,由内积得到
{
    return acos(Dot(A, B) / Length(A) / Length(B));
}
double Area2(Point A, Point B, Point C) //计算两向量构成的平行四边形有向面积,由外积得到
{
    return Cross(B-A, C-A);
}
Vector Rotate(Vector A, double rad) //rad为弧度 且为逆时针旋转的角,计算向量逆时针旋转后的向量
{
    return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}
Vector Normal(Vector A) //向量A左转90°的单位法向量,计算向量逆时针旋转九十度的单位法向量
{
    double L = Length(A);
    return Vector(-A.y/L, A.x/L);
}
bool ToLeftTest(Point a, Point b, Point c) //判断折线bc是不是向ab的逆时针方向(左边)转向
{
    return Cross(b - a, c - b) > 0;
}
Point GetLineIntersection(Point P,Vector v,Point Q,Vector w) //计算两直线交点
{
    Vector u=P-Q;
    double t=Cross(w,u)/Cross(v,w);
    return P+v*t;
}

double DistanceToLine(Point P, Point A, Point B) //计算点P到直线的距离
{
    Vector v1 = B-A, v2 = P-A;
    return fabs(Cross(v1, v2)/Length(v1));
}


double DistanceToSegment(Point P, Point A, Point B) //计算点P到线段AB距离公式
{
    if(A == B)
        return Length(P-A);
    Vector v1 = B-A, v2 = P-A, v3 = P-B;
    if(dcmp(Dot(v1, v2)) < 0)
        return Length(v2);
    if(dcmp(Dot(v1, v3)) > 0)
        return Length(v3);
    return DistanceToLine(P, A, B);
}

Point GetLineProjection(Point P, Point A, Point B) //点P在直线AB上的投影点
{
    Vector v = B-A;
    return A+v*(Dot(v, P-A)/Dot(v, v));
}
struct Circle
{
    double l,r;
};
Circle a[MAXN];

int n;
double x[MAXN],y[MAXN],r[MAXN];
double ans;
bool cmp(Circle a,Circle b)
{
    return a.l<b.l;
}
double dist(int i,int j)
{
    return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
bool contain(int i,int j)//i完全覆盖了j圆,则返回1
{
    if (r[i]-r[j]>=dist(i,j))
        return 1;
    return 0;
}
double cal(int k)
{
    int num=0;
    for (int i=k+1; i<=n; i++)
        if (contain(i,k))//被覆盖了,长度位0
            return 0;
    for (int i=k+1; i<=n; i++)
    {
        if (contain(k,i)||dist(k,i)>=r[i]+r[k])
            continue;
        double d=dist(k,i);
        double ac=acos((-r[i]*r[i]+r[k]*r[k]+d*d)/(2.0*d*r[k]));
        double conta=atan2(y[i]-y[k],x[i]-x[k]);
        Circle l={conta-ac,conta+ac};
        a[++num]=l;
        if (a[num].l<0)
        {
             a[num].l+=2*pi;
        }

        if (a[num].r<0)
        {
            a[num].r+=2*pi;
        }

        if (a[num].l>a[num].r)
        {
            double p=a[num].l;
            a[num].l=0;
            a[++num].l=p;
            a[num].r=2*pi;
        }
    }
    sort(a+1,a+num+1,cmp);
    double res=0.0,now=0.0;
    for (int i=1; i<=num; i++)
    {
        if (a[i].l>now)
            res+=a[i].l-now,now=a[i].r;
        now=max(now,a[i].r);
    }
    res+=2*pi-now;
    return res*r[k];
}
int main()
{
    cin>>n;
    ans=0.0;
    for (int i=1; i<=n; i++)
    {
        cin>>r[i]>>x[i]>>y[i];
    }
    for (int i=1; i<=n-1; i++)
        ans+=cal(i);
    ans+=2*pi*r[n];//最后一个绝对没被覆盖
    printf("%.3lf\n",ans);
}

C++(g++ 7.5.0)(g++7.5.0) 解法, 执行用时: 15ms, 内存消耗: 464K, 提交时间: 2023-08-12 15:44:03

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 1010
#define eps 1e-9
#define MAXDBL 1e20
#define pi acos(-1)
using namespace std;
int n,top;
double ans,sum,temp;
struct Bug
{
    double lb,rb;
    bool operator <(const Bug& a)const
    {
        return lb!=a.lb?lb<a.lb:rb<a.rb;
    }
}sta[MAXN];
struct circle
{
    double x,y,r;
}s[MAXN];
inline double sqr(double x)
{
    return x*x;
}
inline double dis(circle a,circle b)
{
    return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));
}
inline bool in(circle a,circle b)
{
    return a.r>=b.r+dis(a,b);
}
inline void circle_cross_circle(circle a,circle b)
{
    double Dis=dis(a,b),tmp,st,bugle;
    tmp=(sqr(a.r)-sqr(b.r)+sqr(Dis))/(Dis*2);
    st=atan2((a.x-b.x),(a.y-b.y));
    bugle=acos(tmp/a.r);
    sta[++top]=(Bug){(st-bugle),(st+bugle)};
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)  scanf("%lf%lf%lf",&s[i].r,&s[i].x,&s[i].y);
    for (int i=1;i<=n;i++)  
    {
        top=0;sum=0;temp=0;
        bool flag=0;
        for (int j=i+1;j<=n;j++)
            if (in(s[j],s[i]))
            {
                flag=1;
                break;
            }
        if (flag)   continue;
        for (int j=i+1;j<=n;j++)
            if (!in(s[i],s[j])&&s[i].r+s[j].r>=dis(s[i],s[j]))  circle_cross_circle(s[i],s[j]);
        for (int j=1;j<=top;j++)
        {
            while (sta[j].lb<0) sta[j].lb+=2*pi;
            while (sta[j].rb<0) sta[j].rb+=2*pi;
            if (sta[j].lb>sta[j].rb)    sta[++top]=(Bug){0,sta[j].rb},sta[j].rb=2*pi;
        }
        sort(sta+1,sta+top+1);
        for (int j=1;j<=top;j++)
            if (sta[j].lb>temp) sum+=sta[j].lb-temp,temp=sta[j].rb;
            else temp=max(temp,sta[j].rb);
        sum+=2*pi-temp;
        ans+=s[i].r*sum;
    }
    printf("%.3f\n",ans);
}

C++(clang++11) 解法, 执行用时: 49ms, 内存消耗: 496K, 提交时间: 2022-10-27 07:45:18

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

const double PI = acos(-1);
const double PI2 = PI * 2;
const int MAXN = 1010;
const double eps = 1e-6;
int n;
double xs[MAXN], ys[MAXN], rs[MAXN];
typedef std::pair<double, double> PDD;
PDD cover[MAXN]; int bak;
inline double pows(double x) { return x * x; }
inline double absx(double x) { return x < 0 ? -x : x; }
inline double reduce(double x) { x += PI2, x = x - (int) (x / PI2) * PI2; return x; }
int main() {
	scanf("%d", &n);
	double ans = 0;
	for (int i = 1; i <= n; ++i)
		scanf("%lf%lf%lf", rs + i, xs + i, ys + i);
	for (int i = 1; i <= n; ++i) {
		bak = 0;
		for (int j = i + 1; j <= n; ++j) {
			double D = sqrt(pows(xs[i] - xs[j]) + pows(ys[i] - ys[j]));
			if (D >= absx(rs[i] + rs[j]) - eps) continue;
			if (D <= absx(rs[i] - rs[j]) + eps) {
				if (rs[j] > rs[i])
					cover[++bak] = PDD(0, PI2);
				continue;
			}
			double y = (D - (pows(rs[j]) - pows(rs[i])) / D) / 2;
			double angle = acos(y / rs[i]);
			double oa = reduce(atan2(xs[i] - xs[j], ys[i] - ys[j]) + PI / 2);
			double L = reduce(oa - angle), R = reduce(oa + angle);
			if (L - eps > R) cover[++bak] = PDD(L, PI2), cover[++bak] = PDD(0, R);
			else cover[++bak] = PDD(L, R);
		}
		std::sort(cover + 1, cover + 1 + bak);
		ans += PI2 * rs[i];
		double lstl = 0, lstr = 0;
		for (int j = 1; j <= bak; ++j) {
			const double fx = cover[j].first, sx = cover[j].second;
			if (fx > lstr) {
				ans -= (lstr - lstl) * rs[i];
				lstl = fx;
			}
			lstr = std::max(lstr, sx);
		}
		ans -= (lstr - lstl) * rs[i];
	}
	printf("%.3lf\n", ans);
	return 0;
}

上一题