【题意】给了N(N<=2000)个点问能组成多少个锐角三角形?
【解题思路】赛后看别人题解学的,直接统计锐角三角形较困难,考虑问题的反面,统计直角三角形、钝角三角形、平角三角形(暂时这么叫吧QAQ)。
首先枚举三角形的一个端点A,对其他点进行象限为第一关键字,极角为第二关键字排序。
然后使用三个指针,进行O(n)的扫描。
具体做法为用 i 指针指向三角形的第二个端点B。我们可以假想通过平移和旋转,把A点置于平面直角坐标系的原点,把B点置于x轴的正方向。那么可以与AB组成钝角或直角的点就在三四象限或者y轴。
将 j 指针指向第一象限内可以组成锐角的最靠后的点,将k指针从j + 1 开始扫描至最后一个可以组成钝角的点,然后统计对答案的贡献。
之后将 i 指针 +1,继续扫描。
【AC 代码】#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
const double eps=1e-8;
struct Point{
ll x,y;
int f;//代表在哪一个象限
friend Point operator-(Point aa,Point bb){
return (Point){aa.x-bb.x,aa.y-bb.y};
}
}a[2005],b[2005];
Point S;
int n,m;
inline ll cross(Point a,Point b,Point c){//ab X ac
return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);
}
inline int cal(Point a)//计算象限
{
if(a.x>0&&a.y==0) return 1;
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 2;
if(a.x<0&&a.y==0) return 3;
if(a.x<0&&a.y<0) return 3;
if(a.x==0&&a.y<0) return 4;
if(a.x>0&&a.y<0) return 4;
}
inline bool cmp(const Point a,const Point b)//先按象限排序,再按极角排序
{
if(a.f<b.f) return true;
if(a.f>b.f) return false;
ll temp=cross(S,a,b);
if(temp>0) return true;
else return false;
}
inline bool ok(Point a,Point b,Point c)
{
ll temp=(b.x-a.x)*(c.x-a.x)+(b.y-a.y)*(c.y-a.y);
if(temp>0) return true;
else return false;
}
int main()
{
while(scanf("%d",&n)!=EOF)
{
for(int i=1; i<=n; i++){
scanf("%I64d%I64d",&a[i].x,&a[i].y);
}
ll sum=0;//统计除了锐角三角形的其他三角形的个数
for(int p=1; p<=n; p++)//枚举三角形的一个顶点
{
S=a[p];
m=0;
for(int j=1; j<=n; j++){
if(j!=p) b[++m]=a[j];
}
for(int i=1; i<=m; i++) b[i].f=cal(b[i]-S);
sort(b+1,b+m+1,cmp);//极角排序
//two pointers
int i=1,j=1,k=1;
while(ok(S,b[i],b[j])&&cross(S,b[i],b[j])>=0&&j<=m) j++;
if(j==m+1) continue;
j--;k=j+1;
while(i<=m){
if(!ok(S,b[i],b[k])){
while(!ok(S,b[i],b[k+1])&&k<m) k++;
sum+=k-j;
}
i++;
if(j<i) j=i;
while(ok(S,b[i],b[j+1])&&cross(S,b[i],b[j+1])>0&&j<m) j++;
if(k<=j) k=j+1;
if(k>m) break;
}
}
ll ans=1ll*n*(n-1)*(n-2)/6;
ans-=sum;
//cout<<ans<<" "<<sum<<endl;
printf("%I64d\n",ans);
}
}