借鉴大佬:
https://www.cnblogs.com/albert-biu/p/10907996.html
对已有的代码进行详细解释
问题:
如图所示
如果两个点可见的话,那么这两个点在圆上的四个切点存在公共区域,即图一和图二两种情况,所以只需要求出每一个点在圆上的两个切点(用弧度表示,范围【0 , 2*pi】),然后对于这些点进行排序,遍历每一对切点,找他们中间有多少个点,计算出来的结果除以二,为最后答案 即:ans=(∑(0,n) ai)/2
注意:
- atan() 与 atan2() 的区别 第一个范围(-90,90) 第二个范围(-180,180)
- 后边的计算中加上 2pi 为了把范围定位到(0,2pi)
4.lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int maxn=100010;
const double pi=acos(-1);
struct point
{
double x,y;
}a[maxn];
struct node
{
double begin,end;
}e[maxn];
vector<double>s;
int main()
{
int n;
double r;
cin>>n>>r;
for(int i=0;i<n;i++)
{
cin>>a[i].x>>a[i].y;
double len=sqrt(a[i].x*a[i].x+a[i].y*a[i].y);
double r1=acos(r/len);
double r2=atan2(a[i].y,a[i].x);
//注意3
e[i].begin=r2-r1+2*pi;
e[i].end=r2+r1+2*pi;
if(e[i].begin-2*pi>=0) e[i].begin-=2*pi;
if(e[i].end-2*pi>=0) e[i].end-=2*pi;
if(e[i].end-e[i].begin<=0) swap(e[i].end,e[i].begin);
//
s.push_back(e[i].begin);
s.push_back(e[i].end);
}
long long ans=0;
sort(s.begin(),s.end());
long long from,to;
for(int i=0;i<n;i++)
{
from=lower_bound(s.begin(),s.end(),e[i].begin)-s.begin();
to=lower_bound(s.begin(),s.end(),e[i].end)-s.begin();
if(e[i].end-e[i].begin-pi>=0)
{
ans=ans+2*n-(to-from-1)-2;
}
else
{
ans+=to-from-1;
}
}
cout<<ans/2<<endl;
return 0;
}