BZOJ1043 [HAOI2008]下落的圆盘(洛谷P2510)

292 篇文章 1 订阅
281 篇文章 1 订阅

计算几何基础

BZOJ题目传送门
洛谷题目传送门

对于一个圆,它对答案的贡献就是周长-被覆盖的长度,那么重点就是求一个圆被另一个圆覆盖的长度。

我们可以把这个圆沿x正半轴顺时针展开,那么这个圆就变成了一条线段。其它圆的覆盖就变成了线段覆盖了。

两个圆相交的情况如下图所示,利用余弦定理可以求出交点与两圆心连线的夹角,再利用atan2函数求出极角,就可以对应到区间上了。

这里写图片描述

注意判断 <0 < 0 <script type="math/tex" id="MathJax-Element-17"><0</script>或 >2π > 2 π 的情况。

代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1005
#define sqr(x) ((x)*(x))
using namespace std;
typedef double DB;
const DB pi=2*acos(-1.0);
struct seg{ DB l,r; }c[N<<1];
int n,sum;
DB ans,r[N],x[N],y[N];
inline bool cmp(seg a,seg b){ return a.l<b.l; }
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
        scanf("%lf%lf%lf",&r[i],&x[i],&y[i]);
    for (int i=1;i<=n;i++){
        ans+=pi*r[i],sum=0;
        for (int j=i+1;j<=n;j++){
            DB d=sqr(x[i]-x[j])+sqr(y[i]-y[j]);
            if (sqr(r[i]+r[j])<=d)
                c[++sum]=(seg){0,0};
            else
                if (sqr(r[i]-r[j])>=d)
                    c[++sum]=(seg){0,r[i]>r[j]?0:pi};
                else{
                    DB a=acos((r[i]*r[i]-r[j]*r[j]+d)/(2*r[i]*sqrt(d)));
                    DB b=atan2(y[j]-y[i],x[j]-x[i]);
                    if (b<0) b+=pi; c[++sum]=(seg){b-a,b+a};
                    if (c[sum].l<0)
                        c[++sum]=(seg){c[sum-1].l+pi,pi},c[sum-1].l=0;
                    else if (c[sum].r>pi)
                        c[++sum]=(seg){0,c[sum-1].r-pi},c[sum-1].r=pi;
                }
        }
        sort(c+1,c+sum+1,cmp); DB lst=0;
        for (int j=1;j<=sum;j++){
            if (c[j].r<=lst) continue;
            if (c[j].l>lst) ans-=(c[j].r-c[j].l)*r[i];
            else ans-=(c[j].r-lst)*r[i];
            lst=c[j].r;
        }
    }
    return printf("%.3f\n",ans),0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值