题解 P2333 【[SCOI2006]一孔之见】
SuperJvRuo · · 题解
乍一看这题,二分半径,大力simpson啊!看我随手写一个,你看,样例都过了,我交一发......
WA*6+TLE*3+AC*1
半径只能二分解决,而面积,看来不是能用simpson水过的了。
观察图形,利用题中多边形的性质,不难想出这样的搞法:
分别连接多边形的各个顶点与原点,将多边形划分为三角形。每个三角形都对应一个唯一的圆心角,在半径确定时,也就能确定一个对应的扇形。只需求出各个三角形与对应扇形面积交的和即可。
这里分为4种情况:
case 1
最简单的情况,如图中
case 2
稍微难一点的情况,如图中
关于
case 3
前两种的结合,如图中
设两个点分别为
由直线解析式得:
由圆解析式得:
因此有:
解得:
看看哪个点在线段上就行了。然后一侧求三角形面积,另一侧求扇形面积。
case 4
如果解决了前三种,第四种就不是问题了。如图中
是不是很简单啊?
#include<cstdio>
#include<cmath>
#include<algorithm>
#define EPS 1e-5
const double PI=acos(-1);
int n;
double s;
struct Point
{
double dist,x,y;
} point[55];
//求出线段上离原点最近的点,以确定线段与弧是否相交
Point calc_vertical(Point A,Point B)
{
Point cross;
//两种特殊情况:与x轴垂直、与y轴垂直
if(fabs(A.x-B.x)<EPS)
{
cross.x=A.x;
cross.y=0;
cross.dist=fabs(A.x);
}
else if(fabs(A.y-B.y)<EPS)
{
cross.x=0;
cross.y=A.y;
cross.dist=fabs(A.y);
}
else//作垂线
{
double k=(A.y-B.y)/(A.x-B.x);
double b=A.y-k*A.x;
cross.x=-b/(k+1.0/k);
cross.y=-cross.x/k;
cross.dist=sqrt(cross.x*cross.x+cross.y*cross.y);
}
//如果垂足不在线段上,就取两个端点中更近的一个
if(A.x>B.x)
{
std::swap(A,B);
}
if(cross.x<A.x||cross.x>B.x)
{
cross=A.dist<B.dist?A:B;
}
if(A.y>B.y)
{
std::swap(A,B);
}
if(cross.y<A.y||cross.y>B.y)
{
cross=A.dist<B.dist?A:B;
}
return cross;
}
//求线段与圆弧的交点
Point calc_cross(Point A,Point B,double radius)
{
Point res;
if(A.x>B.x)
{
std::swap(A,B);
}
double k=(A.y-B.y)/(A.x-B.x);
double b=A.y-k*A.x;
res.x=(-2*k*b+sqrt(4*k*k*b*b-4*(b*b-radius*radius)*(k*k+1)))/(2*k*k+2);
if(res.x<A.x||res.x>B.x)
{
res.x=(-2*k*b-sqrt(4*k*k*b*b-4*(b*b-radius*radius)*(k*k+1)))/(2*k*k+2);
}
res.y=res.x*k+b;
res.dist=sqrt(res.x*res.x+res.y*res.y);
return res;
}
double mult(Point a,Point b)//向量的向量积的模除以2
{
return fabs(a.x*b.y-b.x*a.y)/2.0;
}
double calc_angle(Point a)//计算极角
{
double cosine=a.x/a.dist;
double res=acos(cosine);
return a.y>0?res:2*PI-res;
}
double calc_sector(Point a,Point b,double radius)//计算扇形面积
{
double angle=fabs(calc_angle(a)-calc_angle(b));
angle=angle<PI?angle:2*PI-angle;
return radius*radius*angle/2.0;
}
double calc_part(Point a,Point b,double radius)//计算面积交
{
if(a.dist-radius<EPS&&b.dist-radius<EPS)
{
return mult(a,b);
}
else
{
if(a.dist>b.dist)
{
std::swap(a,b);
}
Point vertical=calc_vertical(a,b);
if(vertical.dist-radius>EPS)
{
return calc_sector(a,b,radius);
}
else if(a.dist-radius>EPS)
{
return calc_part(a,vertical,radius)+calc_part(vertical,b,radius);
}
else
{
Point cross=calc_cross(a,b,radius);
return calc_sector(cross,b,radius)+mult(a,cross);
}
}
}
double calc_area(double radius)
{
double res=0;
double add;
for(int i=0; i<n; ++i)
{
res+=add=calc_part(point[i],point[i+1],radius);
}
return res;
}
int main()
{
double left_radius=0,right_radius=0,mid_radius;
scanf("%d %lf",&n,&s);
for(int i=0; i<n; ++i)
{
scanf("%lf %lf",&point[i].x,&point[i].y);
point[i].dist=sqrt(point[i].x*point[i].x+point[i].y*point[i].y);
right_radius=std::max(right_radius,point[i].dist);
}
point[n]=point[0];
while(right_radius-left_radius>EPS)//二分半径
{
mid_radius=(left_radius+right_radius)/2.0;
if(calc_area(mid_radius)>s)
{
right_radius=mid_radius;
}
else
{
left_radius=mid_radius;
}
}
printf("%.2lf\n",mid_radius);
return 0;
}