Loading

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

题目大意

给出 \(n\) 个点的坐标,求其中能围成等腰三角形的三个点有几组。

思路

发现 \(n\) 的范围不大,考虑先预处理出每两个点的距离,然后在找出可能成为等腰三角形的点的组数,最后去除多余情况

实现细节

:::info[两点之间的距离公式]
\(a(x_1,y_1)\) 和点 \(b(x_2,y_2)\) 之间的距离为:

\[\sqrt{(x1-x2)^2+(y1-y2)^2} \]

:::

  • 距离的处理
    对于两点之间的距离,我们有必要用浮点数去存储吗?实际上没有,因为如果一个数 \(S\) 确定了,那么 \(\sqrt{S}\) 也是固定的,所以我们只需要记录 \((x1-x2)^2+(y1-y2)^2\) 即可。
  • 可能成为等腰三角形的点的组数
    首先固定顶点,然后如果有 \(2\) 个点到这个点的距离相等,那么这 \(3\) 个点就可能组成等腰三角形。所以我们先把这种情况记录下来。
  • 多余情况
    多余情况无非有 \(2\) 种:
  1. 三点共线
  2. 等边三角形(这种不可能,具体原因自证)

:::info[中点坐标公式]
\(a(x_1,y_1)\) 和点 \(b(x_2,y_2)\) 中点的坐标为:

\[(\frac{x_1+x_2}{2},\frac{y_1+y_2}{2}) \]

:::

注意到,三点共线时一定保证其中一个点是另外两个点的中点,根据中点坐标公式判断即可。

  • tru 的作用
    作用很简单,记录每个点是否存在
  • 时间复杂度
    首先考虑这两层循环:
for(int i=1;i<=n;++i){
  cnt.clear();
  for(int j=1;j<=n;++j){
     ...
  }
}

复杂度 \(\mathcal{O}(n^2)\)
然后是这两层:

for(int i=1;i<=n;++i){
    for(int j=i+1;j<=n;++j)//保证i<j
       ...
}

注意到 \(i=1\) 时内层循环执行 \(n-1\) 次;
\(i=2\) 时内层循环执行 \(n-2\) 次;
\(i=3\) 时内层循环执行 \(n-3\) 次;

\(i=n\) 时内层循环执行 \(0\) 次;
所以这部分复杂度为 \(\mathcal{O}(\frac{n^2-n}{2})\)
所以总复杂度为 \(\mathcal{O}(n^2+\frac{n^2-n}{2})\),可以通过此题。

#include <bits/stdc++.h>
#define ll long long
#define pii pair<int, int>
#define piii pair<pii, int>
#define pll pair<ll, ll>
#define plll pair<pll, ll>
#define pref static inline
#define fi first
#define se second
using namespace std;
const int MAXN=2005;
ll n,x[MAXN],y[MAXN],ans;
map<ll,int>cnt;
map<ll,map<ll,int>>tru;//记录(i,j)位置是否存在点
namespace heffo_hard{
    pref ll dis_2(ll x1,ll y1,ll x2,ll y2){//点(x1,y1)和点(x2,y2)之间的距离,为了方便计算统一把距离定义成dis^2,由于在namespace内,所以y1不会冲突
        return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
    }
    pref void solve(){
        cin>>n;
        for(int i=1;i<=n;++i){
            cin>>x[i]>>y[i];
            tru[x[i]][y[i]]=1;
        }
        for(int i=1;i<=n;++i){
            cnt.clear();
            for(int j=1;j<=n;++j){
                ans+=cnt[dis_2(x[i],y[i],x[j],y[j])];
                cnt[dis_2(x[i],y[i],x[j],y[j])]++;
            }
            for(int j=i+1;j<=n;++j)//保证i<j
                if(!((x[i]+x[j])&1)&&!((y[i]+y[j])&1))
                    ans-=tru[x[i]+x[j]>>1][y[i]+y[j]>>1];//如果存在-1,不存在对ans没有影响
        }
        cout<<ans<<endl;
    }
};

signed main(){
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    heffo_hard::solve();
    return 0;
}
/*
heffo_hard
*/
posted @ 2026-07-11 22:38  heffo_hard  阅读(7)  评论(0)    收藏  举报