Day7 T1 直角三角形

题目

二维平面坐标系中有 \(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 \in \mathbb{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 quad;
};
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].quad=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].quad++;//如果一个点需要顺时针旋转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].quad]++;
                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;
}
posted @ 2019-09-13 13:15  东方澂  阅读(567)  评论(0编辑  收藏  举报