将裁判作为原点,求出原点到每个圆的切点。
将这些切点以及矩形的顶点极角排序,用堆维护最靠近裁判的圆。
对于一段相邻的极角区间,如果没有圆,那么对答案的贡献是三角形的面积。
否则求出两条射线与圆的交点$A,B$,则对答案的贡献是三角形$OAB$的面积减去三角形$OAB$与圆的交的面积。
时间复杂度$O(n\log n)$。
注意因为精度问题,求射线与圆的交点时可能会被判断为不存在交点,此时应该直接用切点。
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long double ld;
typedef pair<int,int>PI;
const int N=1005;
const ld pi=acos(-1.0),eps=1e-9;
int n,m,h,w,w0,i,radius[N],ce,in[N],d[N];ld ans;
priority_queue<PI,vector<PI>,greater<PI> >q;
struct E{ld x;int p;E(){}E(ld _x,int _p,int _t){x=_x,p=_p<<3|_t;}}e[N<<1];
inline bool cmp(const E&a,const E&b){return a.x<b.x;}
inline void add(int x){in[x]=1;q.push(PI(d[x],x));}
inline int sgn(ld x){
if(x<-eps)return -1;
if(x>eps)return 1;
return 0;
}
inline bool Quadratic(ld A,ld B,ld C,ld *t0,ld *t1){
ld discrim=B*B-4.f*A*C;
if(discrim<0.)return 0;
ld rootDiscrim=sqrt(discrim);
ld q;
if(B<0)q=-.5f*(B-rootDiscrim);
else q=-.5f*(B+rootDiscrim);
*t0=q/A;*t1=C/q;
if(*t0>*t1)swap(*t0,*t1);
return 1;
}
struct P{
ld x,y;
P(){x=y=0;}
P(ld _x,ld _y){x=_x,y=_y;}
P operator+(const P&b)const{return P(x+b.x,y+b.y);}
P operator-(const P&b)const{return P(x-b.x,y-b.y);}
P operator*(ld b)const{return P(x*b,y*b);}
P operator/(ld b)const{return P(x/b,y/b);}
ld operator*(const P&b)const{return x*b.x+y*b.y;}
ld len()const{return hypot(x,y);}
ld len_sqr()const{return x*x+y*y;}
P rotate(ld c)const{return P(x*cos(c)-y*sin(c),x*sin(c)+y*cos(c));}
P trunc(ld l)const{return(*this)*l/len();}
}O,left[N],right[N];
inline ld cross(const P&a,const P&b){return a.x*b.y-a.y*b.x;}
inline ld get_angle(const P&a,const P&b){return fabs(atan2(fabs(cross(a,b)),a*b));}
inline P lerp(const P&a,const P&b,ld t){return a*(1-t)+b*t;}
struct circle{
P c;ld r;
circle(){c=P(0,0),r=0;}
circle(P _c,ld _r){c=_c,r=_r;}
}cir[N];
inline void getTangents(const P&p,const circle&C,P&A,P&B){
P u=C.c-p;
ld dist=u.len(),ang=asin(C.r/dist);
u=u.trunc(sqrt(u.len_sqr()-C.r*C.r));
A=u.rotate(-ang)+p;
B=u.rotate(ang)+p;
}
inline bool circle_line_intersection(const circle&c,const P&a,const P&b,ld *t0,ld *t1){
P d=b-a;ld A=d*d;
ld B=d*(a-c.c)*2.0;
ld C=(a-c.c).len_sqr()-c.r*c.r;
return Quadratic(A,B,C,t0,t1);
}
inline ld circle_triangle_intersection_area(const circle&c,const P&a,const P&b){
if(sgn(cross(a-c.c,b-c.c))==0)return 0;
static P q[5];int len=0;ld t0,t1;q[len++]=a;
if(circle_line_intersection(c,a,b,&t0,&t1)){
if(0<=t0&&t0<=1)q[len++]=lerp(a,b,t0);
if(0<=t1&&t1<=1)q[len++]=lerp(a,b,t1);
}
q[len++]=b;ld s=0;
for(int i=1;i<len;i++){
P z=(q[i-1]+q[i])/2;
if((z-c.c).len_sqr()<=c.r*c.r)
s+=fabs(cross(q[i-1]-c.c,q[i]-c.c))/2;
else
s+=c.r*c.r*get_angle(q[i-1]-c.c,q[i]-c.c)/2;
}
return s;
}
inline ld circle_polygon_intersection_area(const circle&c,const P&A,const P&B){
static P v[9];
int n=3;
v[0]=O;
v[1]=A;
v[2]=B;
ld s=0;
for(int i=0;i<n;i++){
int j=(i+1)%n;
s+=circle_triangle_intersection_area(c,v[i],v[j])*sgn(cross(v[i]-c.c,v[j]-c.c));
}
return fabs(s);
}
inline P line_intersection(const P&a,const P&b,const P&p,const P&q){
ld U=cross(p-a,q-p),D=cross(b-a,q-p);
return a+(b-a)*(U/D);
}
inline ld triangle_area(const P&A,const P&B){
return fabs(cross(O,A)+cross(A,B)+cross(B,O))/2;
}
inline void solve(ld L,ld R){
if(L+eps>R)return;
P A=P(cos(L),sin(L))+O;
P B=P(cos(R),sin(R))+O;
while(!q.empty()&&!in[q.top().second])q.pop();
if(q.empty()){
P C,D;
if(m==0)C=P(w,0),D=P(w,h);
else if(m==1)C=P(0,h),D=P(w,h);
else C=P(0,0),D=P(0,h);
ans+=triangle_area(line_intersection(O,A,C,D),line_intersection(O,B,C,D));
return;
}
int x=q.top().second;
ld t0,t1;P _A,_B;
if(circle_line_intersection(cir[x],O,A,&t0,&t1))_A=lerp(O,A,t0);else _A=left[x];
if(circle_line_intersection(cir[x],O,B,&t0,&t1))_B=lerp(O,B,t0);else _B=right[x];
ans+=triangle_area(_A,_B)-circle_polygon_intersection_area(cir[x],_A,_B);
}
int main(){
scanf("%d%d%d%d",&n,&w,&h,&w0);
O=P(w0,0);
e[++ce]=E(atan2(h,w-w0),0,1);
e[++ce]=E(atan2(h,-w0),0,2);
for(i=1;i<=n;i++){
int x,y;
scanf("%d%d%d",&x,&y,&radius[i]);
cir[i]=circle(P(x,y),radius[i]);
int w=(x-w0)*(x-w0)+y*y;
d[i]=w-radius[i]*radius[i];
ld t=atan2(y,x-w0),o=asin(radius[i]/sqrt(w)),l=t-o,r=t+o;
e[++ce]=E(l,i,4);
e[++ce]=E(r,i,5);
P A,B;
getTangents(O,cir[i],A,B);
left[i]=A;
right[i]=B;
}
sort(e+1,e+ce+1,cmp);
for(i=1;i<=ce;i++){
solve(e[i-1].x,e[i].x);
if((e[i].p&7)==4)add(e[i].p>>3);
else if((e[i].p&7)==5)in[e[i].p>>3]=0;
else m=e[i].p&7;
}
solve(e[ce].x,pi);
ans/=h*w;
return printf("%.8f",(double)ans),0;
}
原文:https://www.cnblogs.com/clrs97/p/12163880.html