训练赛 blot

来源:互联网 发布:洛瑞16赛季场均数据 编辑:程序博客网 时间:2024/05/29 11:23

  • 题面
    • 题目大意
    • 输入格式
    • 输出格式
    • 数据范围
  • 题解
  • 代码

题面

题目大意

在地球和太阳之间有一个绕地心公转,绕其自身重心自转的干扰器。干扰器会遮挡阳光。我们将太阳视为一个点光源,地球视为一个圆,而干扰器是一个简单多边形。现在询问在某一时刻,地球上被阳光照射的区域的长度(即圆上被照射到的弧的长度).

输入格式

第一行五个整数,Xs,Ys,Xe,Ye,R,(Xs,Ys),(Xe,Ye),R.
第二行四个整数,n,T1,T2,T,分别表示干扰器上有n个点,干扰器的公转周期,干扰器的自转周期以及询问的时刻。
接下来n行,每行两个整数x,y.按顺时针或逆时针顺序给出干扰器上的n个点.

输出格式

一行,一个实数表示答案,保留两位小数。

数据范围

n30R,T,T1,T2>0104

题解

其实就是一个模拟。不需要用到什么重要的算法,只是要注意一些细节问题.

  • 重心的求法?
  • 将多边形剖分成若干个三角形,每个三角形求一次重心。之后以三角形有向面积为权重,求这些重心的加权平均数即可。
    举个例子:求梯形A(0,0),B(0,12),C(12,12),D(24,0)的重心:
    1.剖分为三角形ABC与三角形ACD.
    2.重心分别为:(4,8)与(12,4).
    3.权重(有向面积):-72与-144
    4.重心坐标(x,y)满足:
    x=4(72)+12(144)(72)+(144)=283
    y=8(72)+4(144)(72)+(144)=163
  • 如果是凹多边形怎么办?
    • 这个没有关系,因为我们是用的有向面积。
  • 公转自转的处理?
    • 利用转轴公式。如果记不住的话,可以利用复平面知识现推:
      (x+yi)(cosϕ+isinϕ)=(xcosϕysinϕ)+(xsinϕ+ycosϕ)i,i.
    • 旋转的问题:
      • 先计算出各点与重心的相对位置,记作向量pi
      • pi按照规定角度旋转,得到pi
      • 旋转重心g
      • 计算g+pi即得到各点的位置.
      • 注意不要先绕重心旋转再绕地心旋转,那样干扰器上各点的自转角度其实变大了。
  • 覆盖问题的处理
    • 遍历所有点,以太阳为基准,按极角排序?
    • 是不是一定要排序?是否可以只取最大值与最小值?
      • atan2的值变化不连续,第二象限角和第三象限角对应的函数值有突变,故不能单纯地取最大值与最小值。
      • 但是我们可以事先将太阳转到x负半轴上,那么所有合法的角必然落在第一象限与第四象限。这样就可以简单地取最值了。
    • 注意地球背面的点不能算在内
    • 计算光线与地球交点的圆心角时,建议利用垂径定理,简化运算。
  • 精度问题:我们要相信cmath库中的函数都是算得很准的。额,其实这个就算是有精度差,我也无能为力。据说有大佬把地球分成了一百万个小段来处理,以取得更高的精度,这种做法我实在不会。

代码

变量名很乱

#include<cmath>#include<cstdio>#include<cstring>#include<iostream>#include<algorithm>using namespace std;const double pi=3.1415926535897932384626433832795;//这个可以用windows自带的计算器算void _r(int& x,bool f=0){    char c=getchar();    while(c<'0'||c>'9')    {        f|=(c=='-');        c=getchar();    }    for(x=0;c>='0'&&c<='9';c=getchar())    {        x=(x<<1)+(x<<3)+c-'0';    }    x=f?-x:x;    return ;}//输入优化struct point//点,向量{    double x,y;    point(double a=0,double b=0)    {        x=a;        y=b;    }    point operator + (point b)    {        return point(x+b.x,y+b.y);    }    point operator - (point b)    {        return point(x-b.x,y-b.y);    }    double operator * (point b)    {        return x*b.y-y*b.x;    }    point rot(double A)    {        double si=sin(A),co=cos(A);        return point(x*co-y*si,x*si+y*co);    }//转轴,弧度制    void print()    {        printf("%.2lf       %.2lf   \n",x,y);    }//调试语句};inline double Area(point a,point b,point c){    return (b-a)*(c-a);}//有向面积point earth,sun,p[50],cen;double R;int T1,T2,T;//公转,自转,time;       int n,X,Y;point cal(){    if(n==1)    {        return p[1];    }    if(n==2)    {        return point((p[1].x+p[2].x)/2.0,(p[1].y+p[2].y)/2.0);    }    double summ=0,_x=0,_y=0,_s=0,tx,ty;    for(int i=2;i<n;i++)    {        _s=Area(p[1],p[i],p[i+1]);        tx=(p[1].x+p[i].x+p[i+1].x)/3.0;        ty=(p[1].y+p[i].y+p[i+1].y)/3.0;        _x+=tx*_s;        _y+=ty*_s;        summ+=_s;    }    return point(_x/summ,_y/summ);}//求重心const double eps=1e-7;double solve(double A){    if(A<0)    {        return -pi/2.0-A+acos(sun.x*sin(A)/R);    }    else    {        return pi/2.0-A-acos(-sun.x*sin(A)/R);    }}//计算交点的圆心角,垂径定理int main(){    //freopen("blot.in","r",stdin);    //freopen("blot.out","w",stdout);    _r(X);    _r(Y);    sun=point(1.0*X,1.0*Y);    _r(X);    _r(Y);    earth=point(1.0*X,1.0*Y);    sun=sun-earth;    double angle=atan2(sun.y,sun.x);    angle=pi-angle;    sun=sun.rot(angle);    _r(X);    R=1.0*X;    _r(n);    scanf("%d%d%d",&T1,&T2,&T);    for(int i=1;i<=n;i++)    {        _r(X);        _r(Y);        p[i]=point(1.0*X,1.0*Y);        p[i]=p[i]-earth;        p[i]=p[i].rot(angle);    }    cen=cal();    int t1=T%T1,t2=T%T2;    angle=2*pi*t2/(1.0*T2);    point tmp;    for(int i=1;i<=n;i++)    {        tmp=p[i]-cen;        p[i]=tmp.rot(angle);    }    angle=2*pi*t1/(1.0*T1);    cen=cen.rot(angle);    for(int i=1;i<=n;i++)    {        p[i]=p[i]+cen;    }    double lim,cenA,mx,mn,S;    cenA=asin(R/-sun.x);//阳光的范围,圆心角    S=pi*R-2*cenA*R;//无遮挡情况下的照射弧长    mx=-cenA;    mn=cenA;    lim=R*R/sun.x;    for(int i=1;i<=n;i++)    {        if(p[i].x<lim)        {            tmp=p[i]-sun;            angle=atan2(tmp.y,tmp.x);            mx=max(mx,angle);            mn=min(mn,angle);        }    }    double L=0,a1,a2;    mx=min(cenA,mx);    mn=max(-cenA,mn);    if(mx>mn)    {        a1=solve(mn);        a2=solve(mx);        L=(a2-a1)*R;    }    printf("%.2lf\n",S-L);    return 0;}
原创粉丝点击