2019CCPCQHD Angle Beats

本文介绍了一种算法,用于解决给定平面上多个点时如何高效地计算每个新增点与原有点能构成的直角三角形数量的问题。通过预处理斜率并使用向量形式存储数据,实现了O(n²logn)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原题链接
题目来源:CCPC2019 秦皇岛站 A题

问题重述

给定二维平面上 n n n个点,然后给出q次询问,每次询问给出一个点P(n+q个点都不同),询问P和之前的n个点能构成多少个直角三角形。
其中 0 ≤ n , q ≤ 2000 ; ∣ x ∣ , ∣ y ∣ ≤ 1 e 9 0 \leq n,q \leq 2000; |x|,|y|\leq 1e9 0n,q2000;x,y1e9

问题分析

假设存在两个点A,B跟这次的这个P构成了直角三角形, 那么可以得到以下的式子:
{ P y − A y P x − A x ⋅ P y − B y P x − B x = − 1 , P 是 顶 点 A y − P y A x − P x ⋅ A y − B y A x − B x = − 1 , P 不 是 顶 点 \begin{cases} {Py-Ay\over Px-Ax} \cdot {Py-By \over Px-Bx} = -1&,P是顶点\\ {Ay-Py\over Ax-Px} \cdot {Ay-By \over Ax-Bx} = -1&,P不是顶点 \end{cases} {PxAxPyAyPxBxPyBy=1AxPxAyPyAxBxAyBy=1,P,P
然后我们发现这个复杂度可以支持 O ( n 2 l o g n ) O(n^2logn) O(n2logn)
分成两个部分来实现:

  1. P是直角顶点,预先处理好P和剩下的n个点的斜率ma。然后枚举一个点A,从ma里面查有没有斜率满足乘积为-1的,然后统计到答案就行了。不过这里要注意,A、B都在n个点之中,所以要统计完了之后除2。
  2. P不是直角顶点,那就枚举一个直角顶点,然后预先处理好和剩下的点的斜率。然后枚举q个询问点作为非直角顶点的一个点,然后查有没有满足斜率相乘为-1的点即可。这里不会算重复,所以放心统计就行了。

下面这个写挂了

  1. 如果P不是直角顶点,那我可以直接枚举另一个直角顶点A,然后在A里面查斜率满足乘积为-1的部分。需要预先处理好任意一个点A对其他点的斜率。预处理复杂度为 O ( n 2 ) O(n^2) O(n2)。不过实现的过程中由于对map不熟导致预处理出来的数据不对。

为啥会WA

  1. 存斜率的时候不要直接就float了,由于斜率一定是整数,可以搞成点对的形式去存(也就是向量)。然后注意统一方向,比如下面的保证 ( x , y ) , x ≥ 0 并 且 x = 0 时 y ≥ 0 (x,y), x\geq 0 并且 x=0时y \geq 0 (x,y),x0x=0y0
  2. 为啥这个题有多组数据?不是没有吗?

代码实现

#include<bits/stdc++.h>
using namespace std;

const int N = 2017;

int n, q;
struct Point{
    long long x, y;
    Point(int a,int b){
        x = a; y = b;
    }
    Point(){
        x = y = 0;
    }
}p1[N], p2[N];
long long ans[N];
struct Vect{
    long long dx, dy;
    void trans(){
        if(dx<0 or (dx==0 and dy<0)){
            dx = -dx; dy = -dy;
        }
    }
    Vect(Point a, Point b){
        dx = a.x - b.x;
        dy = a.y - b.y;
        this->trans();
    }
    Vect(long long a, long long b){
        dx = a; dy = b;
        this->trans();
    }
    bool operator<(const Vect& a)const{
        return dy * a.dx < dx * a.dy;
    }
};
map<Vect, int>ma;

int main(){
    while(~scanf("%d%d",&n,&q)){
    //scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i){
        scanf("%lld%lld",&p1[i].x,&p1[i].y);
    }
    for(int i=1;i<=q;++i){
        scanf("%lld%lld",&p2[i].x,&p2[i].y);
        ans[i] = 0;
    }
    //Do if it is angle 90
    for(int i=1;i<=q;++i){
        ma.clear();
        for(int j=1;j<=n;++j)
            ma[Vect(p1[j], p2[i])]++;
        for(int j=1;j<=n;++j){
            Vect p = Vect(p1[j], p2[i]);
            p = Vect(-p.dy, p.dx);
            ans[i] += ma.count(p)? ma[p] : 0;
        }
        ans[i] /= 2;
    }
    //Do if it is not angle 90
    for(int i=1;i<=n;++i){
        ma.clear();
        for(int j=1;j<=n;++j)
            if(i!=j)    ma[Vect(p1[i], p1[j])]++;
        for(int j=1;j<=q;++j){
            Vect p(p2[j], p1[i]);
            p = Vect(-p.dy, p.dx);
            ans[j] += ma.count(p)? ma[p]:0;
        }
    }
    
    for(int i=1;i<=q;++i)
        printf("%lld\n", ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值