二分题。
在开始之前,我们要先了解一个结论:如果三个点都是整点,那么构不成一个等边三角形。
可以得到证明:
- 设三个整点分别为 A,B,C,则我们可以知道 ,
为有理数,更具夹角公式,不难得出
也为有理数,又因为
为无理数(
)因此三个整点不可能组成等边三角形。
因此我们可以得到一个“相当暴力”的做法:
1. 先对每一个点枚举出她和所有点所连的边(也就是将她作为中心点),并对他们按照长度为第一标准,终点的 $y$ 值为第二标准排序。
2. 然后从中取出长度相同的边(设有 $n$ 条),如果不考虑三点共线的情况,我们可以得出有 种等腰三角形。
3. 因为当我们固定一条边时,我们可以轻而易举的算出共线的时候第三点的坐标(利用中点公式),又因为我们按照 y 为第二字典序排序,所以边长相同的点是按照 y 排序,我们只用二分查找出符合 y 的点,并计算其 x 是否符合就行,如果符合就把答案减一。
- 值得提醒的是:可能有两个 y 值相同的点(因为过一个圆做一条平行于 x 轴的直线,与其交点最多有两个),查找的时候要把两个都找出来。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int F=3000;
struct sl1{
int x,y;
}poi[F];
struct sl2{
int x1,y1,x2,y2;
int len;
}sack[F],work[F];
int top,n,out,clo;
int v[F];
bool cmp(sl2 a,sl2 b){
if(a.len==b.len) return a.y2<b.y2;
return a.len<b.len;
}
int suan(int a,int b){
return pow(poi[a].x-poi[b].x,2)+pow(poi[a].y-poi[b].y,2);
}
void write(int a,int b){
sack[++top].x1=poi[a].x;
sack[top].y1=poi[a].y;
sack[top].x2=poi[b].x;
sack[top].y2=poi[b].y;
sack[top].len=suan(a,b);
}
int work1(int a,int b){
return 2*poi[a].y-work[b].y2;
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>poi[i].x>>poi[i].y;
for(int z1=0,i=1;i<=n;i++,z1=1,top=0){
for(int j=1;j<=n;j++){
if(i==j) continue;
write(i,j);
}//存边
sort(sack+1,sack+1+top,cmp);
while(z1<=top){
int flag=0,top1=0;
while(sack[z1].len==sack[z1+1].len){
work[++top1]=sack[z1];
z1++; flag=1;
}//取长度相同的边
work[++top1]=sack[z1++];
out+=((top1-1)*top1/2);
if(top1>1){
clo++;
for(int k=1;k<=top1;k++){
if(v[k]==clo) continue;
int r=top1,l=1,ans=0,cxk=0,cmk=work1(i,k);
if(cmk<0) continue;
while(r>=l){
int mid=(r+l)/2;
if(work[mid].y2>=cmk){
if(work[mid].y2==cmk) ans=mid;
r=mid-1;
}
else l=mid+1;
}//二分查找
if(ans){
if(work[ans].x2+work[k].x2==poi[i].x*2){
out--;
v[ans]=v[k]=clo;
}
else{
if(work[ans-1].y2==work[ans].y2){
cxk=work[ans-1].x2;
if(cxk+work[k].x2==poi[i].x*2){
out--;
v[ans-1]=v[k]=clo;
}
}
else{
if(work[ans+1].y2==work[ans].y2){
cxk=work[ans+1].x2;
if(cxk+work[k].x2==poi[i].x*2){
out--;
v[ans+1]=v[k]=clo;
}
}
}
}
}//查找是否有第二个y值相同的边
}
}
}
}
cout<<out;
return 0;
}