JPRS编程竞赛2026#1(AtCoder初学者竞赛442)

题干

E - Laser Takahashi

AC代码概览

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
/*题目描述
https://atcoder.jp/contests/abc442/tasks/abc442_e
*/
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 2e5 + 5;

struct Node {
    int x, y, id, p;

    void cal_p() {
        if (!x)
            p = (y > 0) ? 0 : 4;
        else if (!y)
            p = (x > 0) ? 2 : 6;
        else if (x > 0 && y > 0)
            p = 1;
        else if (x > 0 && y < 0)
            p = 3;
        else if (x < 0 && y < 0)
            p = 5;
        else
            p = 7;
    }
} a[maxn];
int n, q, pos[maxn]; 
int pre[maxn], nxt[maxn];

bool cmp(Node a, Node b) {
    if (a.p != b.p)
        return a.p < b.p;
    int v1 = abs(a.y) * abs(b.x);
    int v2 = abs(b.y) * abs(a.x);
    if (a.p == 1 || a.p == 5)
        return v1 > v2;
    return v1 < v2;
}

int32_t main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> n >> q;
    for (int i = 1; i <= n; i++) {
        cin >> a[i].x >> a[i].y;
        a[i].id = i;
        a[i].cal_p();
    }
    sort(a + 1, a + n + 1, cmp);
    for (int i = 1; i <= n; i++) {
        pos[a[i].id] = i;
        if (i > 1 && a[i].p == a[i - 1].p 
                  && a[i].y * a[i - 1].x == a[i - 1].y * a[i].x)
            pre[i] = pre[i - 1];
        else
            pre[i] = i;
    }
    for (int i = n; i >= 1; i--) {
        if (i < n && a[i].p == a[i + 1].p 
                  && a[i].y * a[i + 1].x == a[i + 1].y * a[i].x)
            nxt[i] = nxt[i + 1];
        else
            nxt[i] = i;
    }
    for (int i = 0, a, b, ans; i < q; i++) {
        cin >> a >> b;
        a = pos[a];
        b = pos[b];
        if (pre[a] == pre[b] || a < b)
            ans = nxt[b] - pre[a] + 1;
        else
            ans = nxt[b] + n - pre[a] + 1;
        cout << ans << endl;
    }
    return 0;
}

逐段解析一

cpp 复制代码
struct Node {
    int x, y, id, p;

    void cal_p() {
        if (!x)
            p = (y > 0) ? 0 : 4;
        else if (!y)
            p = (x > 0) ? 2 : 6;
        else if (x > 0 && y > 0)
            p = 1;
        else if (x > 0 && y < 0)
            p = 3;
        else if (x < 0 && y < 0)
            p = 5;
        else
            p = 7;
    }
} a[maxn];

我们将一个坐标系划分成8个部分其中0,2,4,6是坐标轴,1,3,5,7是四象限

cpp 复制代码
bool cmp(Node a, Node b) {
    if (a.p != b.p)
        return a.p < b.p;
    int v1 = abs(a.y) * abs(b.x);
    int v2 = abs(b.y) * abs(a.x);
    if (a.p == 1 || a.p == 5)
        return v1 > v2;
    return v1 < v2;
}
...
sort(a + 1, a + n + 1, cmp);

这里的比较函数首先通过八个部分进行比较,接着在相同部分的情况下,通过斜率进行比较,之所以不用角度或者弧度进行比较是因为后者涉及到小数比较不够精准也不够方便,但是同过斜率的不等式我们很容易得到"乘式",也就是说只需要进行整数之间的比较。这样做的本质原因是题目中要求顺时针,再加上数据量足够我们进行nlog(n)的遍历,所以我们有必要先对所有坐标进行一个顺时针的排序

逐段解析二

cpp 复制代码
    for (int i = 1; i <= n; i++) {
        pos[a[i].id] = i;
        if (i > 1 && a[i].p == a[i - 1].p 
                  && a[i].y * a[i - 1].x == a[i - 1].y * a[i].x)
            pre[i] = pre[i - 1];
        else
            pre[i] = i;
    }
    for (int i = n; i >= 1; i--) {
        if (i < n && a[i].p == a[i + 1].p 
                  && a[i].y * a[i + 1].x == a[i + 1].y * a[i].x)
            nxt[i] = nxt[i + 1];
        else
            nxt[i] = i;
    }

1, pos[a[i].id] = i;

这一段是为了给排好序的a[i]中的每个坐标给他们标好rank,至此排序结束。

2,其余部分是 为了解决题目中所提到的可能出现在同一射线上的点,也就是在进行区间统计时如果不进行适当的操作会造成边界部分损失的问题,本质上是没有定义适当的边界,那么我们通过向前遍历所有的点可以找出在同一条射线上的点并将他们分组,这个组是前边沿组,也就是这个组的值是最前边沿的点的编号,同理通过向前遍历所有的点我们可以的到末边沿的组,这个组的值是最末边沿的点的编号。这样以来我们就可以确定最适当的边沿(最大区间,避免损失)。

逐段解析三

cpp 复制代码
for (int i = 0, a, b, ans; i < q; i++) {
        cin >> a >> b;
        a = pos[a];
        b = pos[b];
        if (pre[a] == pre[b] || a < b)
            ans = nxt[b] - pre[a] + 1;
        else
            ans = nxt[b] + n - pre[a] + 1;
        cout << ans << endl;
    }

最后一部分也就是输出部分,根据每个查询给出的两个坐标编号,我们先确定他们在顺时针中的实际顺序:
cin >> a >> b;
a = pos[a];
b = pos[b];

接下来我们需要确定他们的位置关系:

1,在一条射线上

2,左小右大

3,右小左大

其中1,2情况处理方法一致,只需要大减小得出中间右多少个

第三个情况需要先用2的方法做再加个n(可以看成用n减去情况三)

相关推荐
王老师青少年编程2 小时前
信奥赛C++提高组csp-s之倍增算法思想及应用(3)
c++·noip·csp·信奥赛·csp-s·提高组·倍增算法
老鼠只爱大米2 小时前
LeetCode经典算法面试题 #21:合并两个有序链表(迭代法、原地合并法等多种实现方案详解)
算法·leetcode·链表·优先队列·迭代法·合并两个有序链表·原地合并
源代码•宸2 小时前
Leetcode—47. 全排列 II【中等】
经验分享·后端·算法·leetcode·面试·golang·深度优先
wen__xvn2 小时前
基础算法集训第20天:Dijkstra
算法·图论
万象.2 小时前
redis客户端安装与实现C++版本
数据库·c++·redis
Yiyaoshujuku2 小时前
疾病的发病率、发病人数、患病率、患病人数、死亡率、死亡人数查询网站及数据库
数据库·人工智能·算法
wen__xvn2 小时前
基础算法集训第18天:深度优先搜索
算法·深度优先·图论
jiang_changsheng3 小时前
comfyui节点插件笔记总结新增加
人工智能·算法·计算机视觉·comfyui
TracyCoder1233 小时前
LeetCode Hot100(7/100)—— 3. 无重复字符的最长子串
算法·leetcode