题意:二维平面上n个点,问从中选三个点能构成三角形的方案数?
n<=2e3, -100<=x[i],y[i]<=100.
任选三个C(n,3) 扣除掉选三在同一条直线上的方案.
先枚举dx,dy 然后在选一个点作为起点 查看这条直线上有多少个点.dx,dy要互质 否则一条直线会遍历多次.
注意以p[i]为起点的直线上过去有点被标记,那么要先扣除这v1个点方案 然后在加上新的点tot个的方案(为了维护一条直线上的最多点).O(m^2*nlogn)[m==100]
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e3+5,M=100;
ll n,x[N],y[N],cx[N],cy[N];
int mk[N][N];
ll C(ll n,ll m)
{
if(n<m)
return 0;
return (n*(n-1)*(n-2))/6;
}
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
struct node{
int x,y;
}p[N];
bool vis[N];
bool cmp(node a,node b)
{
if(a.x==b.x)
return a.y<b.y;
return a.x<b.x;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>p[i].x>>p[i].y;
p[i].x+=M,p[i].y+=M;
cx[p[i].x]++,cy[p[i].y]++;
}
sort(p+1,p+1+n,cmp);
for(int i=1;i<=n;i++)
mk[p[i].x][p[i].y]=i;
ll res=C(n,3);
for(int dx=1;dx<=100;dx++)
{
for(int dy=1;dy<=100;dy++)
{
if(gcd(dx,dy)!=1)
continue;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
{
if(vis[i]) continue;
ll tot=0,x=p[i].x,y=p[i].y,v1=0;
while(x<=200&&y<=200)
{
if(mk[x][y])
{
tot++;
if(!vis[mk[x][y]])
vis[mk[x][y]]=true;
else
v1++;
}
x+=dx,y+=dy;
}
res-=C(tot,3);
res+=C(v1,3);
}
}
}
for(int dx=1;dx<=100;dx++)
{
for(int dy=-100;dy<0;dy++)
{
if(gcd(dx,-dy)!=1)
continue;
memset(vis,0,sizeof(vis));
for(int i=n;i>=1;i--)
{
if(vis[i]) continue;
ll tot=0,x=p[i].x,y=p[i].y,v1=0;
while(x<=200&&y>=0)
{
if(mk[x][y])
{
tot++;
if(!vis[mk[x][y]])
vis[mk[x][y]]=true;
else
v1++;
}
x+=dx,y+=dy;
}
res-=C(tot,3);
res+=C(v1,3);
}
}
}
for(int i=0;i<=2*M+10;i++)
res-=C(cx[i],3),res-=C(cy[i],3);
cout<<res<<'\n';
return 0;
}
标准解法:
可以先枚举一点p[i],剩下两个点随便选,然后在扣除掉选两个共线的情况.
现在想知道有多少个点和p[i]共线,
只要维护最简[gcd(dy,dx)==1]的二元组(dy,dx)的个数,遍历一遍点集即可
注意合法的三元组(a,b,c) 在枚举a,b,c时都出现一次 所以最后答案要除以三 O(n^2logn).
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii;
typedef long long ll;
const int N=2e3+5,M=100;
ll n,x[N],y[N],cx[N],cy[N];
map<ii,int> mk;
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
ll calc(ll x)
{
if(x<2)
return 0;
return x*(x-1)/2;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
cin>>x[i]>>y[i];
ll res=0;
for(int i=1;i<=n;i++)
{
mk.clear();
for(int j=1;j<=n;j++)
{
if(i==j) continue;
int dx=x[j]-x[i],dy=y[j]-y[i];
if(dx==0)
dy=1;
else if(dy==0)
dx=1;
else
{
int d=gcd(abs(dy),abs(dx));
dy/=d,dx/=d;
if(dy<0)
dy=-dy,dx=-dx;
}
mk[ii(dy,dx)]++;
}
res+=(n-1)*(n-2)/2;//last two random
for(auto it=mk.begin();it!=mk.end();it++)
res-=calc(it->second);
}
cout<<res/3<<'\n';
return 0;
}