タイトル
二次元平面座標系は、N点を有しています。N点から選択された3点は、直角三角形を形成するような方法は、これら三つの点から選択され、どのように多くの質問しました。
エントリー
最初の行は、整数Nを含んでいる(図3 (\ \ leqslantの\) N (\ \ leqslantの\) 1500)、ドット数。
次のN個の行は、各点の空間座標によって分離された2つの整数を含む各行は座標値を表す- (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と、座標系翻訳しました 今、それは第一象限と判定された各点のために、次いで、90°(k∈Z)•Kを回転させる、それが第一象限に入ります。その後、点ソートすべての点にわたる傾きk正比例関数(縦分割横軸)に応じました。同じ2つの点の勾配と隣接する象限場合は、回転前直角頂点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;
}