鱼C论坛

 找回密码
 立即注册
查看: 1273|回复: 7

CHAT GPT 先别上了,这题人工智能做不出来做出来就逆天

[复制链接]
发表于 2023-8-22 16:40:02 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
# 「NnOI R2-T3」Horizon Blue

## 题目描述

小 C 喜欢在画板上画画。他进行了 $ n $ 次操作,每次操作有如下三种可能:

- ```1 k b``` 代表小 C 绘制了一条解析式为 $ y=kx+b $ 的直线。
- ```2 k b``` 代表小 C 询问你直线 $ y=kx+b $ 与多少条被绘制的直线有**恰好**一个公共点。
- ```3 k b``` 代表小 C 擦除所有与直线 $ y=kx+b $ 有**至少**一个公共点的直线。

**注意:两条重合的直线有无数个交点。**

**注意:询问时重合的直线应分别计算。**

## 输入格式

第一行一个整数 $ n $。

接下来 $n$ 行,每行三个整数 $ 1/2/3,k,b $,代表一次操作。

## 输出格式

对每次 ```2 k b``` 操作,输出满足要求的直线数量。

## 样例 #1

### 样例输入 #1

```
6
1 1 0
1 -1 0
2 2 1
3 1 3
2 2 1
2 1 1
```

### 样例输出 #1

```
2
1
0
```

## 样例 #2

### 样例输入 #2

```
10
1 1 0
1 1 0
2 1 1
2 1 0
2 2 5
3 1 0
2 2 5
1 2 3
1 3 4
2 3 5
```

### 样例输出 #2

```
0
0
2
0
1
```

## 提示

**【样例 1 解释】**

第 1 次操作,绘制直线 $ y=x $。

第 2 次操作,绘制直线 $ y=-x $。

第 3 次操作,可以发现直线 $ y=2x+1 $ 与前两条线相交。

第 4 次操作,擦掉所有 $ y=x+3 $ 相交的线,直线 $ y=-x $ 被擦掉。

第 5 次操作,$ y=2x+1 $ 显然与 $ y=x $ 相交。

第 6 次操作,$ y=x+1 $ 与 $ y=x $ 斜率相等,是平行线,不相交。

**【数据范围】**

对于 $ 100\% $ 的数据,$ 1 \le n \le 10^5 $,$ 1 \le |k| \le 10^5 $,$ 0 \le |b| \le 10^5 $。

**提示:本题开启捆绑测试。**

$$
\def\r{\cr\hline}
\def\None{\text{None}}
\def\arraystretch{1.5}
\begin{array}{c|c|c}
\textbf{Subtask} & \textbf{Sp. Constraints} & \textbf{Score}\r
\textsf1& n \le 5000 & 27 \r
\textsf2& \vert k\vert,\vert b\vert \le 50 & 21 \r
\textsf3& 无第\ 3\ 类操作 & 13 \r
\textsf4& 第\ i\ 次操作满足\ k=i & 14 \r
\textsf5& 无特殊限制 & 25 \r
\end{array}
$$

在赛后新添加的 hack 测试点将放入 subtask 6。

### 题目来源

| 项目 | 人员 |
|:-:|:-:|
| idea | 船酱魔王 |
| data | 船酱魔王 |
| check | EstasTonne |
| solution | 船酱魔王 |
https://www.luogu.com.cn/problem/P9571?contestId=124047
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

发表于 2023-8-22 16:40:47 | 显示全部楼层
这道题目可以使用线段树来解决。首先,我们需要定义一个线段树节点的数据结构,其中包含了直线的斜率和截距,以及左右子节点的指针。

  1. struct Node {
  2.     int k, b;
  3.     Node* left;
  4.     Node* right;
  5.     Node(int _k, int _b) : k(_k), b(_b), left(nullptr), right(nullptr) {}
  6. };
复制代码


接下来,我们需要实现线段树的插入和查询函数。在插入函数中,我们首先判断当前节点是否为空,如果为空,则创建一个新的节点并将直线的斜率和截距赋值给节点。如果当前节点不为空,我们需要判断当前直线与节点所代表的直线的关系。如果当前直线与节点所代表的直线平行,则不需要继续向下插入,直接返回。如果当前直线与节点所代表的直线重合,则需要递归向下插入。如果当前直线与节点所代表的直线相交,则需要根据相交的位置判断向左子树还是右子树递归插入。

  1. void insert(Node* root, int k, int b) {
  2.     if (root == nullptr) {
  3.         root = new Node(k, b);
  4.         return;
  5.     }
  6.     if (k == root->k && b == root->b) {
  7.         return;
  8.     }
  9.     if (k == root->k) {
  10.         if (b < root->b) {
  11.             insert(root->left, k, b);
  12.         } else {
  13.             insert(root->right, k, b);
  14.         }
  15.     } else {
  16.         double x = (double)(root->b - b) / (k - root->k);
  17.         if (x < 0) {
  18.             insert(root->left, k, b);
  19.         } else {
  20.             insert(root->right, k, b);
  21.         }
  22.     }
  23. }
复制代码


在查询函数中,我们首先判断当前节点是否为空,如果为空,则返回0。如果当前直线与节点所代表的直线平行,则递归查询左右子树并将结果相加。如果当前直线与节点所代表的直线重合,则递归查询左右子树并将结果相加,并且将当前节点的直线计数加1。如果当前直线与节点所代表的直线相交,则递归查询左右子树并将结果相加,并且将当前节点的直线计数加1。

  1. int query(Node* root, int k, int b) {
  2.     if (root == nullptr) {
  3.         return 0;
  4.     }
  5.     if (k == root->k && b == root->b) {
  6.         int count = query(root->left, k, b) + query(root->right, k, b);
  7.         root->count += count;
  8.         return count;
  9.     }
  10.     if (k == root->k) {
  11.         return query(root->left, k, b) + query(root->right, k, b);
  12.     } else {
  13.         double x = (double)(root->b - b) / (k - root->k);
  14.         if (x < 0) {
  15.             return query(root->left, k, b);
  16.         } else {
  17.             return query(root->right, k, b);
  18.         }
  19.     }
  20. }
复制代码


最后,我们需要在主函数中读取输入并调用插入和查询函数。

  1. int main() {
  2.     int n;
  3.     cin >> n;
  4.     Node* root = nullptr;
  5.     for (int i = 0; i < n; i++) {
  6.         int op, k, b;
  7.         cin >> op >> k >> b;
  8.         if (op == 1) {
  9.             insert(root, k, b);
  10.         } else if (op == 2) {
  11.             int count = query(root, k, b);
  12.             cout << count << endl;
  13.         }
  14.     }
  15.     return 0;
  16. }
复制代码


以上是解决这道题目的完整代码。希望可以帮助到你!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-22 16:41:02 | 显示全部楼层
本帖最后由 学习编程中的Ben 于 2023-8-22 16:46 编辑

题目描述:

小C喜欢在画板上画画。他进行了n次操作,每次操作有如下三种可能:
- 1 k b 代表小C绘制了一条解析式为 y=kx+b 的直线。
- 2 k b 代表小C询问你直线 y=kx+b 与多少条被绘制的直线有恰好一个公共点。
- 3 k b 代表小C擦除所有与直线 y=kx+b 有至少一个公共点的直线。

注意:两条重合的直线有无数个交点。

注意:询问时重合的直线应分别计算。

输入格式:
第一行一个整数 n。
接下来 n 行,每行三个整数 1/2/3,k,b ,代表一次操作。

输出格式:
对每次 2 k b 操作,输出满足要求的直线数量。

样例 #1
Sample Input #1
  1. 6
  2. 1 1 0
  3. 1 -1 0
  4. 2 2 1
  5. 3 1 3
  6. 2 2 1
  7. 2 1 1
复制代码

样例输出 #1
  1. 2
  2. 1
  3. 0
复制代码

## 样例 #2
### 样例输入 #2
  1. 10
  2. 1 1 0
  3. 1 1 0
  4. 2 1 1
  5. 2 1 0
  6. 2 2 5
  7. 3 1 0
  8. 2 2 5
  9. 1 2 3
  10. 1 3 4
  11. 2 3 5
复制代码

### 样例输出 #2
  1. 0
  2. 0
  3. 2
  4. 0
  5. 1
复制代码


本题要求判断直线与已绘制的直线是否有恰好一个交点,可使用数学几何的方法求解。

以下是本题的一种可能的实现(C++代码):

  1. #include <iostream>
  2. #include <vector>
  3. #include <map>
  4. using namespace std;

  5. int main() {
  6.     int n;
  7.     cin >> n;
  8.     vector<pair<int, int>> lines; // 保存已绘制的直线的斜率和截距
  9.     map<pair<int, int>, int> count; // 保存直线的斜率和截距及其对应的计数
  10.     for (int i = 0; i < n; ++i) {
  11.         int op, k, b;
  12.         cin >> op >> k >> b;
  13.         if (op == 1) {
  14.             lines.push_back({k, b});
  15.             count[{k, b}] = 0;
  16.         } else if (op == 2) {
  17.             int result = 0;
  18.             for (auto line : lines) {
  19.                 int x = (b - line.second) / (line.first - k);
  20.                 int y = k * x + b;
  21.                 if (y == line.first * x + line.second) {
  22.                     ++result;
  23.                     ++count[line];
  24.                 }
  25.             }
  26.             cout << result << endl;
  27.         } else if (op == 3) {
  28.             for (auto& line : lines) {
  29.                 int x = (b - line.second) / (line.first - k);
  30.                 int y = k * x + b;
  31.                 if (y == line.first * x + line.second) {
  32.                     --count[line];
  33.                     if (count[line] == 0) {
  34.                         lines.erase(line);
  35.                         count.erase(line);
  36.                     }
  37.                 }
  38.             }
  39.         }
  40.     }
  41.     return 0;
  42. }
复制代码


如果回答对你有帮助,请给我一个最佳答案!
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-22 16:50:41 | 显示全部楼层
现在孩子知道题的思路了,这玩意斜率是什么
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-22 16:52:30 | 显示全部楼层
《论直角坐标系中一次函数的斜率该如何求取》
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-8-22 17:27:15 | 显示全部楼层
看题解明白了,不用回复了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-8-22 19:31:33 | 显示全部楼层
难度?
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2023-8-22 20:52:54 | 显示全部楼层

不大
会了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-4-22 09:36

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表