题意:
平面上n个点,问能组成多少个锐角三角形
枚举每个点作为三角形的一个点i,然后对剩下的点计较排序,
以点i与另一个点j 为一条边,去扫描边【i-j】左边有多少个锐角记p1,
再扫描左边一共可以构成多少锐角,钝角,直角,两者相减就是钝角直角的数量p2
每次on扫描的话,总复杂度n^3会超时,可以用two-pointer维护,因为j转移到j+1时,锐角的数量可以在j的基础继续统计,同理,总的角数p2也可以再前者基础上继续统计,那样总的复杂度就是n^2
还有一层排序,所以应该是n^2 logn
</pre><pre name="code" class="cpp">#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn=2000+50;
const double eps=1e-8;
const double PI=acos(-1.0);
struct point
{
ll x,y;
point(ll x=0,ll y=0):x(x),y(y) {}
point operator - (const point &t)const
{
return point(x-t.x,y-t.y);
}
ll operator * (const point &t)const /// 叉积 : 有向面积
{
return x*t.y-y*t.x;
}
ll operator ^ (const point &t)const /// 点积 : 有向长度
{
return x*t.x+y*t.y;
}
bool operator < (const point &t)const /// 极角排序
{
bool up[2]= {0,0};
if(y>0 || (y==0 && x>0)) up[0]=1;
if(t.y>0 || (t.y==0 && t.x>0)) up[1]=1;
if(up[0]^up[1]) return up[0];
return (*this)*t ? (*this)*t>0 : ((*this)^(*this))<(t^t);
}
};
point p[maxn],v[maxn*2];
int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
for(int i=1; i<=n; i++)
scanf("%lld%lld",&p[i].x,&p[i].y);
ll ans=1LL*n*(n-1)*(n-2)/6,tmp=0;
for(int i=1; i<=n; i++)
{
int cun=0;
for(int j=1; j<=n; j++)
{
if(i==j) continue;
v[++cun]=p[j]-p[i]; /// 向量i->j
}
sort(v+1,v+1+cun);
for(int j=1; j<=cun; j++) v[j+cun]=v[j];
///两条向量共线且同向->三点共线,计算会重复2次
for(int j=2; j<=cun; j++)
{
ll num=0;
while(j<=cun&&v[j-1]*v[j]==0 && (v[j-1]^v[j])>0)
j++,num++;
num++;
tmp+=num*(num-1)/2;
}
int p1=1,p2=1;
for(int j=1; j<=cun; j++)
{
while(p1<=j || (p1<j+cun && v[p1]*v[j]<0 && (v[p1]^v[j])>0)) p1++;/// v[j]左边的锐角个数
while(p2<=j || (p2<j+cun && v[p2]*v[j]<0))p2++;/// v[j]左边的锐角+钝角+直角个数
ans-=p2-p1;
}
}
printf("%lld\n",ans-tmp/2);
}
return 0;
}