题面
题目大意
在地球和太阳之间有一个绕地心公转,绕其自身重心自转的干扰器。干扰器会遮挡阳光。我们将太阳视为一个点光源,地球视为一个圆,而干扰器是一个简单多边形。现在询问在某一时刻,地球上被阳光照射的区域的长度(即圆上被照射到的弧的长度).
输入格式
第一行五个整数,
Xs,Ys,Xe,Ye,R,表示太阳坐标(Xs,Ys),地心坐标(Xe,Ye),地球半径R.
第二行四个整数,
n,T1,T2,T
,分别表示干扰器上有n个点,干扰器的公转周期,干扰器的自转周期以及询问的时刻。
接下来n行,每行两个整数x,y.按顺时针或逆时针顺序给出干扰器上的n个点.
输出格式
一行,一个实数表示答案,保留两位小数。
数据范围
n≤30,R,T,T1,T2>0,所有数字绝对值不大于104
题解
其实就是一个模拟。不需要用到什么重要的算法,只是要注意一些细节问题.
- 重心的求法?
- 将多边形剖分成若干个三角形,每个三角形求一次重心。之后以三角形有向面积为权重,求这些重心的加权平均数即可。
举个例子:求梯形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)=283y=8∗(−72)+4∗(−144)(−72)+(−144)=163 - 如果是凹多边形怎么办?
- 这个没有关系,因为我们是用的有向面积。
- 公转自转的处理?
- 利用转轴公式。如果记不住的话,可以利用复平面知识现推:
(x+yi)(cosϕ+isinϕ)=(xcosϕ−ysinϕ)+(xsinϕ+ycosϕ)i,其中i为虚数单位. - 旋转的问题:
- 先计算出各点与重心的相对位置,记作向量 pi
- 将 pi 按照规定角度旋转,得到 p′i
- 旋转重心 g
- 计算
g+p′i 即得到各点的位置. - 注意不要先绕重心旋转再绕地心旋转,那样干扰器上各点的自转角度其实变大了。
- 利用转轴公式。如果记不住的话,可以利用复平面知识现推:
- 覆盖问题的处理
- 遍历所有点,以太阳为基准,按极角排序?
- 是不是一定要排序?是否可以只取最大值与最小值?
- 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;
}