题目链接:点击查看
题目大意:给出 n 个点,计算有多少个锐角三角形
题目分析:直接枚举点计算的话不太好办,因为 n 给到了 2000 ,直接枚举的话复杂度到了 n^3 ,但正难则反,我们考虑一下锐角三角形是由三个锐角组成的,而钝角三角形和直角三角形是由两个锐角 + 一个非锐角组成的,所以我们可以先计算出所有锐角的个数 ans ,以及所有非锐角的个数 not_ans ,那么答案显然是 ( ans - not_ans * 2 ) / 3 了
这样问题就稍微简单一点了,我们只需要O(n)枚举第一个点,然后极角排序+双指针确定下来剩下的两个点,就能计算角的个数了,实现比较简单,需要注意细节就是 x 和 y 在这个题目中到了 1e9 的程度,而 atan( 1e9 , -1e9 ) 则更是到了1e-18的精确度,但也不至于这么极限,总之eps需要稍微开大点,大概开到 1e9 的100倍,也就是 1e-11 就足够了
2020.9.16更新:
关于这种统计三角形个数的题目,用atan来对极角排序的话,丢精度丢的太明显了,所以用叉积又写了一发
代码:
按照atan排序
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e3+100;
const double eps = 1e-11;
const double pi = acos(-1.0);
int sgn(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;
}
void input(){
scanf("%lf%lf",&x,&y);
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
}point[N];
vector<double>node;
int n;
int get_num(double angle)
{
int r=0,ans=0;
for(int l=0;l<n-1;l++)
{
int cnt=0;//计算有多少个共线的点
while(sgn(node[l+cnt]-node[l])==0)
cnt++;
l+=cnt-1;//最后一个共线的点
r=max(r,l+1);
while(r-l+1<n&&sgn(node[r]-node[l]-angle)<0)
r++;
ans+=(r-l-1)*cnt;//注意点 r 是不满足要求的,所以答案是(r-1)-l个
}
return ans;
}
int main()
{
//#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
//#endif
// ios::sync_with_stdio(false);
while(scanf("%d",&n)!=EOF)
{
LL ans=0,not_ans=0;
for(int i=1;i<=n;i++)
point[i].input();
for(int i=1;i<=n;i++)
{
node.clear();
for(int j=1;j<=n;j++)
{
if(i==j)
continue;
Point t=point[j]-point[i];
node.push_back(atan2(t.y,t.x));
}
sort(node.begin(),node.end());
for(int i=0;i<n-1;i++)//令范围增加一倍,从[0,2*pi]到[0,4*pi]
node.push_back(node[i]+2*pi);
int temp=get_num(pi/2);//求出有多少个锐角
ans+=temp;
not_ans+=get_num(pi)-temp;//求出有多少个直角/钝角
}
printf("%lld\n",(ans-2*not_ans)/3);
}
return 0;
}
按照叉积排序
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<unordered_map>
#define double LL
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e3+100;//顶点数
LL ans,not_ans;
int n;
int sgn(double x){
if(x==0)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;
}
void input(){
scanf("%lld%lld",&x,&y);
}
bool operator == (Point b)const{
return sgn(x-b.x) == 0 && sgn(y-b.y) == 0;
}
bool operator < (Point b)const{
return sgn(x-b.x)== 0?sgn(y-b.y)<0:x<b.x;
}
Point operator -(const Point &b)const{
return Point(x-b.x,y-b.y);
}
Point operator +(const Point &b)const{
return Point(x+b.x,y+b.y);
}
//叉积
double operator ^(const Point &b)const{
return x*b.y - y*b.x;
}
//点积
double operator *(const Point &b)const{
return x*b.x + y*b.y;
}
//返回两点的距离
double distance(Point p){
return (x-p.x)*(x-p.x)+(y-p.y)*(y-p.y);
}
}p[N],q[N<<1];
int getxx(Point& a)
{
if(a.x>0 && a.y>=0) return 1;
if(a.x<=0 && a.y>0) return 2;
if(a.x<0 && a.y<=0) return 3;
if(a.x>=0 && a.y<0) return 4;
}
bool cmp(Point a,Point b)
{
if(getxx(a)!=getxx(b))
return getxx(a)<getxx(b);
return sgn(a^b)>0;
}
void solve(int id)
{
for(int i=0,j=0;i<n;i++)
if(i!=id)
q[j++]=p[i]-p[id];
sort(q,q+n-1,cmp);
int n=::n-1;
for(int i=0;i<n;i++)
q[i+n]=q[i];
int j=0,k=0,l=0;//相同极角的点的位置,锐角极角的位置,钝角极角的位置
for(int i=0;i<n;i++)
{
while(j<i+n&&(q[i]^q[j])==0&&(q[i]*q[j])>0)
j++;
k=max(k,j);
while(k<i+n&&(q[i]^q[k])>0&&(q[i]*q[k])>0)
k++;
l=max(l,k);
while(l<i+n&&(q[i]^q[l])>0)
l++;
ans+=k-j;
not_ans+=l-k;
}
}
int main()
{
#ifndef ONLINE_JUDGE
// freopen("data.in.txt","r",stdin);
// freopen("data.out.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
while(scanf("%d",&n)!=EOF)
{
ans=0,not_ans=0;
for(int i=0;i<n;i++)
p[i].input();
for(int i=0;i<n;i++)
solve(i);
printf("%lld\n",(ans-2*not_ans)/3);
}
return 0;
}