bzoj 1043 下落的圆盘 —— 求圆心角、圆周长
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1043
求出每个圆没被覆盖的长度即可;
特判包含和相离的情况,注意判包含时 i 包含 j 和 j 包含 i 是不同的情况;
然后考虑相交,可以算出被覆盖的那段圆弧所对的圆心角,用一个 [0,2π] 的角度区间维护没被覆盖的部分;
所求的角度是对于一条“基准线”而言的,所以首先要求出圆心连线对于“基准线”的角度,因为知道两个圆心,可以利用 atan2(y,x) 求出 tan(θ) = y/x 对应的 θ
然后求圆弧的两个端点的角度,发现已知三边,可以用余弦定理;
求出角度,覆盖区间,最后在 [0,2π] 上找出没被覆盖的区间长度,就能算了。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<vector> using namespace std; typedef double db; db const Pi=acos(-1.0),eps=1e-8; int const xn=1005; int n; db ans; struct N{db x,y,r;}p[xn]; struct P{ db l,r; P(db l=0,db r=0):l(l),r(r) {} bool operator < (const P &y) const {return l<y.l;} }v[xn]; db sqr(db x){return x*x;} int dmp(db x){if(fabs(x)<=eps)return 0; return x>eps?1:-1;} db dis(N a,N b){return sqrt(sqr(a.x-b.x)+sqr(a.y-b.y));} int main() { scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%lf%lf%lf",&p[i].r,&p[i].x,&p[i].y); for(int i=1;i<=n;i++) { bool fl=0; int cnt=0; for(int j=i+1;j<=n;j++) { db x1=p[i].x,x2=p[j].x,y1=p[i].y,y2=p[j].y,r1=p[i].r,r2=p[j].r,d=dis(p[i],p[j]); if(dmp(d+p[i].r-p[j].r)<=0){fl=1; break;}//i in j if(dmp(d+p[j].r-p[i].r)<=0||dmp(d-p[i].r-p[j].r)>=0)continue;//j in i db fx=atan2(y2-y1,x2-x1); db th=acos((sqr(r1)+sqr(d)-sqr(r2))/(2*r1*d));//acos db l=fx-th,r=fx+th; while(l<0)l+=2*Pi; while(r<0)r+=2*Pi; while(l>2*Pi)l-=2*Pi; while(r>2*Pi)r-=2*Pi; if(dmp(l-r)<=0)v[++cnt]=P(l,r); else v[++cnt]=P(0,r),v[++cnt]=P(l,2*Pi); } if(fl)continue;// sort(v+1,v+cnt+1); db mx=0,g=0; for(int j=1;j<=cnt;j++) { if(dmp(v[j].r-mx)<=0)continue; if(dmp(v[j].l-mx)>0)g+=v[j].l-mx; mx=v[j].r; } ans+=p[i].r*(g+2*Pi-mx);// } printf("%.3f\n",ans); return 0; }