题目描述
样例
input:
4
3 3
-3 3
-3 -3
3 -3
5
2 -1 2 1
output:
0.6098
题目大意
在坐标系中给出一个凸多边形,并可以绕原点旋转。现有一条线段,要求凸多边形旋转某个度数后,线段不能够将凸多边形截成两半(如果线段不够长就无法做到)的概率。
分析
很多dalao都是用三分做的,比较麻烦。这题有更加简洁的方式。
可以考虑精度误差。由于题目只需要我们保留4位小数,在时间充足的情况下,完全可以枚举。
枚举多边形转的度数 ,然后可以用计算几何判断一下就可以了。
然后就是转整个的多边形比较麻烦,所以可以转线段。而转线段我们可以理解成这条线段向原点的距离的线段旋转后,再在端点作垂线。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=7e4+10;
const double eps = 1e-8;
const double PI = acos(-1.0);
int sign(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
else return 1;
}//判断正负
struct Point
{
double x,y;
Point(){}
Point(double _x,double _y){x=_x;y=_y;}
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;}//叉积
double operator *(Point b){return x*b.y+y*b.x;}//点积
Point operator %(double a){return Point(x*a,y*a);}//数乘
void input(){scanf("%lf%lf",&x,&y);}
};
struct Line
{
Point s,e;
Line(){}
Line(Point _s,Point _e){s=_s;e=_e;}
void linput(){s.input();e.input();}
};
Point rot(Point a,double Ct){
return Point(a.x*cos(Ct)-a.y*sin(Ct),a.x*sin(Ct)+a.y*cos(Ct));
}//将点a绕着原点旋转Ct角度
Point inter(Line a,Line b){
return a.s+(a.e-a.s)%(((b.e-b.s)^(b.s-a.s))/((b.e-b.s)^(a.e-a.s)));
}//求直线a,b的交点
double dist(Point a,Line b){
Point v1=b.e-b.s,v2=a-b.s;
return abs(v1^v2)/sqrt(v1.x*v1.x+v1.y*v1.y);
}//点a到直线b的距离
double norm(Point a){
return sqrt(a.x*a.x+a.y*a.y);
}//模
Point vp[MAXN<<2];
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
vp[i].input();vp[n+i]=vp[n*2+i]=vp[i];
//这里要存3倍,枚举的时候可能跑了一圈,然后找相交的时候又跑了一圈,最后的指针可能落在第三圈
}
double d,len;scanf("%lf",&len);
Line s;s.linput();
d=dist(Point(0,0),s);//到原点的距离
int cnt=0,tot=3e5,ptr1=1,ptr2=1;
for(int i=0;i<tot;i++){
double al=i*2*PI/tot;//旋转总共的1/tot角度
Point cur=Point(cos(al),sin(al))%d;//旋转后的点,乘上数乘就是从原点向这个点作了垂线
Point v=rot(cur,PI/2);//再旋转90°,这样可以复原出旋转后的线段
Point nxt=cur+v;//注意上一行中只是向量,需要加上长度变成边
Point p1,p2;Line ss=Line(cur,nxt);
while(1){//判断与第一条边的相交
int o1=sign((nxt-cur)^(vp[ptr1]-nxt));
int o2=sign((nxt-cur)^(vp[ptr1+1]-nxt));//得出是否与这一条和下一条相交
//当且仅当一条相交且下一条不相交时,才说明当前这条相交
if(o1*o2==1){ptr1++;continue;}//否则继续寻找下一条边
p1=inter(ss,Line(vp[ptr1],vp[ptr1+1]));//否则就记录交点,并退出
break;
}ptr2=max(ptr2,ptr1+1);//后面一条的编号要更大
while(1){
int o1=sign((nxt-cur)^(vp[ptr2]-nxt));
int o2=sign((nxt-cur)^(vp[ptr2+1]-nxt));
if(o1*o2==1){ptr2++;continue;}
p2=inter(ss,Line(vp[ptr2],vp[ptr2+1]));
break;
}//同理继续找与第二条的相交
double res=norm(p1-p2);//得出旋转后的相交的需要的长度
if(sign(res-len)>0) cnt++;//如果不够长,则无法做到
}printf("%.4f\n",1.0*cnt/tot);
}
END
暴力出奇迹