Link
注意到合法的一对三角形一定存在一对内公切线。
那么我们得到了一个(O(n^3))的做法:枚举一条公切线的两个端点,计算公切线左侧和右侧的点数并统计答案。
考虑优化,先固定公切线的一端,然后把另一端按照该公切线的极角排序,那么利用前缀和就可以做到(O(n^2log n))了。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using db=double;
using i64=long long;
const int N=3007;const db pi=acos(-1);
struct node{int x,y,c;db k;}a[N];
int s[2][3],p[N],x[N],y[N],c[N];
int operator<(const node&a,const node&b){return a.k<b.k;}
db mod(db x){return x<=0? x+pi:x;}
int read(){int x;scanf("%d",&x);return x;}
int main()
{
int n=read();i64 ans=0;
for(int i=1;i<=n;++i) x[i]=read(),y[i]=read(),c[i]=read();
for(int t=1;t<=n;++t)
{
for(int i=1,j=0;i<=n;++i) if(i^t) a[++j]={x[i]-x[t],y[i]-y[t],c[i],mod(atan2(y[i]-y[t],x[i]-x[t]))};
std::sort(a+1,a+n),memset(s,0,24);
for(int i=1;i<n;++i) ++s[p[i]=a[i].y>0||(!a[i].y&&a[i].x<0)][a[i].c];
for(int i=1;i<n;++i)
{
--s[p[i]][a[i].c];i64 r1=1,r2=1;
for(int j=0;j<3;++j)
{
if(c[t]^j) r1*=s[0][j],r2*=s[1][j];
if(a[i].c^j) r1*=s[1][j],r2*=s[0][j];
}
ans+=r1+r2,++s[p[i]^=1][a[i].c];
}
}
printf("%lld
",ans/4);
}