P9423 [蓝桥杯 2023 国 B] 数三角

二分题。

在开始之前,我们要先了解一个结论:如果三个点都是整点,那么构不成一个等边三角形。

可以得到证明:

- 设三个整点分别为 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;
}


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值