题目
二维平面坐标系中有N个点。从N个点选择3个点,问有多少选法使得这3个点形成直角三角形。
输入
第一行包含一个整数N(3\(\leqslant\)N$\leqslant\(1500),表示点数。 接下来N行,每行包含两个用空格隔开的整数表示每个点的坐标,坐标值在\)-10^9$到\(10^9\)之间。每个点位置互不相同。
输出
输出直角三角形的数量。
样例
输入 | 输出 |
---|---|
3 4 2 2 1 1 3 |
1 |
4 5 0 2 6 8 6 5 7 |
0 |
5 -1 1 -1 0 0 0 1 0 1 1 |
7 |
题解
固定一个点P,平移整个坐标系,使得P为原点。现在,对于每个点,首先确定其所在的象限,然后将其旋转k•90°(k∈Z),使其落在第一象限中。之后,按照过点的正比例函数的斜率k(纵坐标除以横坐标)对所有点进行排序。如果两个点斜率相同并且旋转之前在相邻的象限中,它们就能形成以P为直角顶点的直角三角形。排序后,对于每一组斜率相同的点,统计它们原来在每个象限的点的个数,并将相邻象限的点的数量相乘。时间复杂度为\(O(N^2logN)\)。(这就是为什么\(O(N^3)\)的暴力枚举在加一堆玄学优化后也能卡过:因为正解的时间复杂度也不低)
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n; int x[1510],y[1510]; ll ans=0; struct node{ ll x,y; int qua; }; node cpy[1510]; bool cmp(node a,node b){ return a.y*b.x<a.x*b.y; //将不等式变形成两边都是乘法运算,避免除法运算带来的精度损失 } void rotate(int m){ if(cpy[m].x==0 && cpy[m].y==0) return;//如果是原点,无需旋转 cpy[m].qua=1; while(cpy[m].x<=0 || cpy[m].y<0){ swap(cpy[m].x,cpy[m].y); cpy[m].y=-cpy[m].y; cpy[m].qua++;//如果一个点需要顺时针旋转n次才落在第一象限,那么其原象限为n+1 //最多旋转3次(原来在第四象限),所以这里无需像隔壁熊泽恩同学写的那样取模 } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d %d",&x[i],&y[i]); for(int i=1;i<=n;i++){ for(int j=1;j<=n;j++){ cpy[j].x=x[j]-x[i]; cpy[j].y=y[j]-y[i]; rotate(j); } swap(cpy[1],cpy[i]);//不要把原点给算进去了 sort(cpy+2,cpy+n+1,cmp); int j=2; while(j<=n){ int cnt[5]={0}; int k=j; while(k<=n && cpy[j].y*cpy[k].x==cpy[j].x*cpy[k].y){ cnt[cpy[k].qua]++; k++; } for(int t=1;t<4;t++) ans+=cnt[t]*cnt[t+1]; ans+=cnt[1]*cnt[4]; j=k; } } printf("%lld",ans); return 0; }