2178: 圆的面积并

本文介绍了一种使用辛普森积分方法来解决特定几何问题的算法实现,该问题涉及计算一系列圆之间的有效线段长度。通过预处理阶段去除完全被其他圆覆盖的圆,并调整圆的位置来逼近连续函数,最终采用辛普森积分计算总面积。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:如题所示


这大概可以用辛普森积分求。。。?

定义函数f(x)为在这个位置上有效线段长度,有效线段就是直线被圆覆盖的区域

然后这不是个连续函数,为了减小误差,可以适当缩小圆与圆直接的距离,但不能影响答案

这样就差不多是个连续函数了。。。然后还可以去掉那些完全被包含的圆

预处理完以后在暴力辛普森积分

此题的eps要取到1E-13。。。并且常数不能太大

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<algorithm>
using namespace std;
 
const int maxn = 1010;
typedef double DB;
const DB EPS = 1E-13;
const DB INF = 1E16;
 
int cmp(const DB &x,const DB &y)
{
    if (fabs(x - y) <= EPS) return 0;
    return x < y ? -1 : 1;
}
 
struct Point{
    DB x,y; Point(){}
    Point(DB x,DB y): x(x),y(y){}
    Point operator + (const Point &B) {return Point(x + B.x,y + B.y);}
    Point operator - (const Point &B) {return Point(x - B.x,y - B.y);}
};
typedef Point Vector;
 
struct Circle{
    Point o; DB r; Circle(){}
    Circle(Point o,DB r): o(o),r(r){}
    bool operator < (const Circle &B) const {return o.x - r < B.o.x - B.r;}
}c[maxn];
 
struct data{
    DB w; int Num,typ; data(){}
    data(DB w,int Num,int typ): w(w),Num(Num),typ(typ){}
    bool operator < (const data &B) const
    {
        int d = cmp(w,B.w);
        if (!d) return typ > B.typ;
        return d < 0;
    }
}D[maxn*2];
 
struct Range{
    DB l,r; Range(){}
    Range(DB l,DB r): l(l),r(r){}
    bool operator < (const Range &B) const
    {
        int d = cmp(l,B.l);
        if (!d) return r < B.r;
        return d < 0;
    }
}h[maxn*2];
 
int n,tp,sum;
bool bo[maxn];
DB last;
map <DB,DB> M;
 
DB Dot(Vector v1,Vector v2) {return v1.x * v2.x + v1.y * v2.y;}
DB Length(Vector v) {return sqrt(Dot(v,v));}
DB Cal(const DB &c,const DB &a) {return sqrt(c * c - a * a);}
bool Judge(Circle A,Circle B) {return cmp(A.r,Length(A.o - B.o) + B.r) >= 0;}
 
DB F(DB x)
{
    if (M.count(x)) return M[x];
    DB ret = 0; tp = 0;
    for (int i = 1; i <= n; i++)
    {
        DB d = fabs(c[i].o.x - x);
        if (cmp(c[i].r,d) <= 0) continue;
        DB g = Cal(c[i].r,d);
        h[++tp] = Range(c[i].o.y - g,c[i].o.y + g);
    }
    sort(h + 1,h + tp + 1);
    int cur = 1; last = h[1].r;
    for (int i = 2; i <= tp; i++)
    {
        int d = cmp(h[i].l,last);
        if (d <= 0) last = max(last,h[i].r);
        else
        {
            ret += last - h[cur].l;
            cur = i; last = h[i].r;
        }
    }
    ret += last - h[cur].l;
    return M[x] = ret;
}
 
DB Calc(DB L,DB R)
{
    DB A = (R - L) / 6.00;
    DB B = F(L) + 4.00 * F((L + R) / 2.00) + F(R);
    return A * B;
}
 
DB Simpson(DB L,DB R,DB now)
{
    DB mid = (L + R) / 2.00,ret;
    DB A = Calc(L,mid),B = Calc(mid,R);
    if (!cmp(A + B,now)) ret =  now;
    else ret = Simpson(L,mid,A) + Simpson(mid,R,B);
    M.erase(mid); return ret;
}
 
void Pre_Work(DB &L,DB &R)
{
    for (int i = 1; i <= n; i++)
        if (!bo[i]) c[++tp] = c[i];
    n = tp; tp = 0; sort(c + 1,c + n + 1);
    for (int i = 1; i <= n; i++)
    {
        L = min(L,c[i].o.x - c[i].r);
        R = max(R,c[i].o.x + c[i].r);
        D[++tp] = data(c[i].o.x - c[i].r,i,1);
        D[++tp] = data(c[i].o.x + c[i].r,i,-1);
    }
    sort(D + 1,D + tp + 1); last = D[1].w;
    for (int i = 1; i <= tp; i++)
    {
        if (!sum && D[i].typ == 1)
        {
            DB delta = D[i].w - last;
            for (int j = D[i].Num; j <= n; j++) c[j].o.x -= delta;
        }
        if (sum == 1 && D[i].typ == -1) last = D[i].w;
        sum += D[i].typ;
    }
}
 
int main()
{
     
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        DB x,y,r;
        scanf("%lf%lf%lf",&x,&y,&r);
        c[i] = Circle(Point(x,y),r);
        for (int j = 1; j < i; j++)
        {
            if (Judge(c[i],c[j])) bo[j] = 1;
            else if (Judge(c[j],c[i])) bo[i] = 1;
        }
    }
    DB L = INF,R = 0; Pre_Work(L,R);
    printf("%.3lf\n",Simpson(L,R,Calc(L,R)));
    //cerr << (DB)(clock()) / CLOCKS_PER_SEC << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值