题意:二维平面上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; }