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;
}

 

posted @ 2018-12-19 18:07  Zinn  阅读(203)  评论(0编辑  收藏  举报