SPOJ CIRU SPOJ VCIRCLE 圆的面积并问题

给出若干圆 求面积并
找到所有边界,然后格林公式,就是里面的面积。

tip:

正方向为逆时针,对于所给的N个圆,我们可以进行去冗杂,表现为:
(1) 去掉被包含(内含 or 内切)的小圆 ()
(2) 去掉相同的圆

枚举每个圆,并对于剩下的所有圆和它求交点,对于所求的的交点,可以得到一个角度区间 [A,B], 当然区间如果跨越了(例如 [1.5PI, 0.5PI],注意这里是有方向的) 2PI那么需要拆 区间
可以知道,最后区间的并集必然是最后 所有圆和当前圆的交集的一个边界!
所有区间取交集,左区间排序,取当前最大right,left如果大于这个值,中间就是空的区间。。(这段区间合并是google一个面试题)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps = 1e-8;
const double pi = acos(-1.0);
const int maxn = 1e6+10;
struct Tpoint{
    double x,y;
};
typedef pair<double,double> pdd;
pdd now[maxn];
struct Tcircle{
    Tpoint p;
    double r;
};

Tpoint operator -(Tpoint x,Tpoint y){
    Tpoint tmp;
    tmp.x = x.x-y.x;tmp.y = x.y-y.y;
    return tmp;
}
Tpoint operator +(Tpoint x,Tpoint y){
    Tpoint tmp;
    tmp.x = x.x+y.x;tmp.y = x.y+y.y;
    return tmp;
}
Tpoint operator *(Tpoint x,double d){
    Tpoint tmp ;
    tmp.x = x.x*d;tmp.y = x.y*d;
    return tmp;
}
Tpoint operator /(Tpoint x,double d){
    Tpoint tmp ;
    tmp.x = x.x/d;tmp.y = x.y/d;
    return tmp;
}
double operator *(Tpoint x,Tpoint y){
    return x.x*y.x + x.y*y.y;
}
double operator ^(Tpoint x,Tpoint y){
    return x.x*y.y-x.y*y.x;
}
double dis0(Tpoint a){
    return sqrt(a.x*a.x+a.y*a.y);
}

int sign(double k){
    if(fabs(k) < eps)  return 0;
    return k > 0 ? 1:-1;
}

double getpoint(Tpoint ap,double ar,Tpoint bp,double br){
    double d = dis0(ap-bp);
    double cos_t = (ar*ar+d*d-br*br)/(2*ar*d);
    return cos_t;
}
int m,n;
Tcircle tcin[maxn],c[maxn];

void uni(){
    n=0;
    for(int i=1;i<=m;i++){
        bool covered=false;
        for(int j=1;j<=m;j++) if(i!=j){
            int tmpr=sign(tcin[j].r-tcin[i].r);
            int tmp=sign(dis0(tcin[i].p-tcin[j].p)-(tcin[j].r-tcin[i].r));
            if(tmp<=0){
                if(tmpr>0 || (tmpr==0&&j<i)) covered=true;
            }
        }
        if(!covered) c[++n]=tcin[i];
    }
}
double to_2pi(double x){
    if(x < 0)   return x+2*pi;
    else    return x;
}
void sov(){
    double ans = 0;
    for(int i = 1 ; i <= n ; i++){
        int cnt = 0;double a = c[i].p.x,b = c[i].p.y,r = c[i].r;
        for(int j = 1 ; j <= n ; j++){
            if(i == j) continue;
            if(sign(dis0(c[i].p-c[j].p)-(c[i].r+c[j].r)) >= 0) continue;
            double cos_t = getpoint(c[i].p,c[i].r,c[j].p,c[j].r);
            double stad,angle1,angle2;
            stad = atan2((c[j].p-c[i].p).y,(c[j].p-c[i].p).x);
            angle1 = to_2pi(stad - acos(cos_t));
            angle2 = to_2pi(stad + acos(cos_t));
            if(sign(angle2-angle1) >= 0){
                now[cnt++] = make_pair(angle1 , angle2);
            }
            else{
                now[cnt++] = make_pair(0.0 , angle2);
                now[cnt++] = make_pair(angle1 , 2*pi);
            }
        }

        sort(now,now+cnt);
        double right = 0;
        for(int k = 0 ; k < cnt ; k++){
            if(now[k].first > right){
                double low = right, up = now[k].first;
                ans += 0.5*r*r*(up-low) + 0.5*r*(a*(sin(up)-sin(low))-b*(cos(up)-cos(low)));
            }
            right = fmax(right , now[k].second);
        }
        double low = right,up = 2*pi;
        ans += 0.5*r*r*(up-low) + 0.5*r*(a*(sin(up)-sin(low))-b*(cos(up)-cos(low)));
    }
    printf("%.3f\n",ans);
}

int main(){
    scanf("%d",&m);
    for(int i = 1; i <= m; i++)
        scanf("%lf%lf%lf",&tcin[i].p.x,&tcin[i].p.y,&tcin[i].r);
    uni();
    sov();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值