3034. 望远镜
题目链接
3034. 望远镜
Updog 正在用望远镜观察一个飞行物。
望远镜的视野可以描述为一个圆,其圆心位于原点,半径为 \(R\)。
飞行物可视作一个 \(N\) 个顶点的简单多边形。
Updog 希望知道飞行物处于望远镜视野之内的部分的面积。
输入格式
本题包含多组测试数据。
对于每组数据,第一行包含一个实数 \(R\)。
第二行包含一个整数 \(N\)。
接下来 \(N\) 行,每行包含两个实数 \(x_i,y_i\),表示一个顶点的坐标。相邻两行描述的顶点在多边形中也是相邻的。
输出格式
每组数据输出一行一个实数,表示答案。
结果四舍五入保留两位小数。
数据范围
每个测试点最多包含 \(10\) 组数据。
\(3 \le N \le 50\),
\(0.1 \le R \le 1000\),
\(-1000 \le x_i,y_i \le 1000\)
输入样例:
10
3
0 20
10 0
-10 0
输出样例:
144.35
解题思路
三角剖分
三角剖分是一种类似于求任意一个多边形面积的算法,即将一个多边形分为若干个三角形,且这些三角形都有一个公共点,沿着某个方面求解得到的面积和即为多边形的面积
本题要求求一个圆和多边形的面积交,可以将圆的圆心作为公共点,将多边形三角剖分,本题即求解若干个三角形和圆的面积交的和,且这些三角形都有一个端点在圆心上,然后分如下几种情况讨论计算即可:(感谢 Anoxia_3 同学的图)
- 时间复杂度:\(O(n)\)
代码
// Problem: 望远镜
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/3037/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=55;
const double eps=1e-8,pi=acos(-1);
typedef pair<double,double> PDD;
int n;
double R;
PDD a[N],p;
PDD operator+(PDD a,PDD b)
{
return {a.fi+b.fi,a.se+b.se};
}
PDD operator-(PDD a,PDD b)
{
return {a.fi-b.fi,a.se-b.se};
}
PDD operator*(PDD a,double t)
{
return {a.fi*t,a.se*t};
}
PDD operator/(PDD a,double t)
{
return {a.fi/t,a.se/t};
}
double operator*(PDD a,PDD b)
{
return a.fi*b.se-b.fi*a.se;
}
double operator&(PDD a,PDD b)
{
return a.fi*b.fi+a.se*b.se;
}
int sign(double x)
{
if(fabs(x)<eps)return 0;
if(x<0)return -1;
return 1;
}
int dcmp(double x,double y)
{
if(fabs(x-y)<eps)return 0;
if(x<y)return -1;
return 1;
}
double area(PDD a,PDD b,PDD c)
{
return (b-a)*(c-a);
}
double get_len(PDD a)
{
return sqrt(a&a);
}
double get_dist(PDD a,PDD b)
{
return get_len(b-a);
}
PDD rotate(PDD a,double angle)
{
return {a.fi*cos(angle)+a.se*sin(angle),-a.fi*sin(angle)+a.se*cos(angle)};
}
PDD get_line_intersection(PDD p,PDD v,PDD q,PDD w)
{
PDD u=p-q;
double t=w*u/(v*w);
return p+v*t;
}
bool on_segment(PDD p,PDD a,PDD b)
{
return !sign((p-a)*(p-b))&&sign((p-a)&(p-b))<=0;
}
PDD norm(PDD a)
{
return a/get_len(a);
}
double get_circle_line_intersection(PDD a,PDD b,PDD &pa,PDD &pb)
{
PDD o=get_line_intersection(a,b-a,p,rotate(b-a,pi/2));
double mind=get_dist(p,o);
if(!on_segment(o,a,b))mind=min(get_dist(p,a),get_dist(p,b));
if(dcmp(R,mind)<=0)return mind;
double len=sqrt(R*R-get_dist(p,o)*get_dist(p,o));
pa=o+norm(a-b)*len;
pb=o+norm(b-a)*len;
return mind;
}
double get_sentor(PDD a,PDD b)
{
double angle=acos((a&b)/get_len(a)/get_len(b));
if(sign(a*b)<0)angle=-angle;
return R*R*angle/2;
}
double get_circle_triangle_area(PDD a,PDD b)
{
double da=get_dist(p,a),db=get_dist(p,b);
if(dcmp(da,R)<=0&&dcmp(db,R)<=0)return a*b/2;
if(!sign(a*b))return 0;
PDD pa={0,0},pb={0,0};
double mind=get_circle_line_intersection(a,b,pa,pb);
if(dcmp(R,mind)<=0)return get_sentor(a,b);
if(dcmp(R,da)>=0)return a*pb/2+get_sentor(pb,b);
if(dcmp(R,db)>=0)return pa*b/2+get_sentor(a,pa);
return get_sentor(a,pa)+pa*pb/2+get_sentor(pb,b);
}
double triangulate()
{
double res=0;
for(int i=0;i<n;i++)res+=get_circle_triangle_area(a[i],a[(i+1)%n]);
return fabs(res);
}
int main()
{
while(~scanf("%lf%d",&R,&n))
{
for(int i=0;i<n;i++)scanf("%lf%lf",&a[i].fi,&a[i].se);
printf("%.2lf\n",triangulate());
}
return 0;
}