










GESP C++ 四级/五级练习题,排序与双指针(滑动窗口/尺取法)及二分查找的经典应用。题目要求在 $10^6$ 规模下统计所有距离不超过 $d$ 的奶牛坐标数对。考察将 $\mathcal{O}(n^2)$ 暴力枚举优化为 $\mathcal{O}(n \log n)$ 算法的能力,以及防范 32 位整型溢出与快速输入输出技巧。难度⭐⭐。洛谷难度等级普及-。
在你的养牛场,所有的奶牛都养在一排呈直线的牛栏中。一共有 $n$ 头奶牛,其中第 $i$ 头牛在直线上所处的位置可以用一个整数坐标 $p_i(0\le p_i \le 10^8)$ 来表示。在无聊的日子里,奶牛们常常在自己的牛栏里与其它奶牛交流一些八卦新闻。每头奶牛发出的声音响度是一样的,而由于声波的能量衰减,某头奶牛发出的声音只能被与它距离不超过 $d(0 \le d \le 10^4)$ 的奶牛所听到,这样这对奶牛就称为可以相互交流的。现在给出所有奶牛的位置和声音所能传播的最远距离 $d$ ,请你编个程序来计算你的养牛场里究竟有多少对可以相互交流的奶牛。
第一行包含两个整数 $n,d$。
第二行包含 $n$ 个整数,每个整数都是一个坐标 $p_i$,描述一头奶牛在直线上的位置。
一个数,表示养牛场中可以相互交流奶牛的对数。
数据规模:
| 本题属于一维数轴上的 区间数对统计问题。给定 $n$ 个点的坐标,要求统计满足距离 $ | p_i - p_j | \le d$($i \ne j$)的无序数对对数。 |
最直观的方法是两重循环枚举所有可能的奶牛对 $(i, j)$($0 \le i < j < n$):
| 判断坐标之差是否满足 $ | p_i - p_j | \le d$; |
复杂度与效率评估:
既然数轴上的距离计算只与相对大小有关,我们可以先将所有奶牛的坐标按升序排序。
假设排序后的坐标数组为 $p[0], p[1], \dots, p[n-1]$,此时满足 $p[0] \le p[1] \le \dots \le p[n-1]$。 对于任意固定的左端点奶牛 $i$,合法的右侧奶牛 $j$($j > i$)必须满足: \(p[j] - p[i] \le d\)
由于数组是单调不降的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
坐标数组 (升序): [ 10, 12, 16, 37, 40 ] (d = 10)
▲ ▲
│ │
i = 0 j = 2 (p[2]-p[0]=6 <= 10, p[3]-p[0]=27 > 10)
以 i=0 为起点的合法对数: j - i = 2 - 0 = 2 (配对: 12, 16)
-------------------------------------------------------
[ 10, 12, 16, 37, 40 ]
▲ ▲
│ │
i = 1 j = 2 (p[2]-p[1]=4 <= 10)
以 i=1 为起点的合法对数: j - i = 2 - 1 = 1 (配对: 16)
-------------------------------------------------------
[ 10, 12, 16, 37, 40 ]
▲
│ (i=2, j=2)
以 i=2 为起点的合法对数: j - i = 2 - 2 = 0
-------------------------------------------------------
[ 10, 12, 16, 37, 40 ]
▲ ▲
│ │
i = 3 j = 4 (p[4]-p[3]=3 <= 10)
以 i=3 为起点的合法对数: j - i = 4 - 3 = 1 (配对: 40)
-------------------------------------------------------
总合法对数 = 2 + 1 + 0 + 1 = 4
++j;除了双指针,利用已排序数组的单调性,还可以使用二分查找直接定位右边界:
std::upper_bound 在区间 $[i + 1, n - 1]$ 中查找第一个严格大于 $p[i] + d$ 的位置 it;it - (p.begin() + i + 1);long long)int 的最大值约为 $2.14 \times 10^9$。$5 \times 10^{11}$ 远超 int 范围,若使用 int 存储答案会产生整型溢出(变成负数或错误值)。ans 必须声明为 long long 类型!std::cin 速度较慢可能引起超时。main 函数开头添加快速输入输出语句:1
2
ios::sync_with_stdio(false);
cin.tie(nullptr);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 开启 I/O 加速
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, d;
cin >> n >> d;
vector<int> p(n);
for (int i = 0; i < n; ++i) {
cin >> p[i];
}
// 1. 坐标升序排序
sort(p.begin(), p.end());
// 2. 双指针扫描统计
long long ans = 0; // 必须使用 long long 防止溢出
int j = 0;
for (int i = 0; i < n; ++i) {
// 右指针向右滑动,直到超出距离 d 或到达边界
while (j + 1 < n && p[j + 1] - p[i] <= d) {
++j;
}
// 与当前奶牛 i 配对的合法奶牛数为 j - i
ans += (j - i);
}
cout << ans << "\n";
return 0;
}
upper_bound 二分查找1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 开启 I/O 加速
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, d;
cin >> n >> d;
vector<int> p(n);
for (int i = 0; i < n; ++i) {
cin >> p[i];
}
// 1. 坐标升序排序
sort(p.begin(), p.end());
// 2. 对每个元素二分查找最远合法右边界
long long ans = 0; // 必须使用 long long
for (int i = 0; i < n; ++i) {
// 在 [i + 1, n) 范围内查找第一个 > p[i] + d 的位置
auto it = upper_bound(p.begin() + i + 1, p.end(), p[i] + d);
// 合法区间为 [p.begin() + i + 1, it)
ans += (it - (p.begin() + i + 1));
}
cout << ans << "\n";
return 0;
}
| 方法 | 排序耗时 | 统计耗时 | 总体时间复杂度 | 额外空间复杂度 | 适用场景 |
|---|---|---|---|---|---|
| 暴力枚举 | 无 | $\mathcal{O}(n^2)$ | $\mathcal{O}(n^2)$ | $\mathcal{O}(1)$ | 仅限 $n \le 10^3$(得 40 分) |
| 排序 + 二分查找 | $\mathcal{O}(n \log n)$ | $\mathcal{O}(n \log n)$ | $\mathcal{O}(n \log n)$ | $\mathcal{O}(1)$ | 思路直观,代码简洁 |
| 排序 + 双指针 | $\mathcal{O}(n \log n)$ | $\mathcal{O}(n)$ | $\mathcal{O}(n \log n)$ | $\mathcal{O}(1)$ | 最优解法,常数小,运行极快 |
备考技巧归纳:
long long。所有代码已上传至Github:https://github.com/lihongzheshuai/yummy-code
GESP 学习专题站:GESP WIKI
"luogu-"系列题目可在洛谷题库进行在线评测。
"bcqm-"系列题目可在编程启蒙题库进行在线评测。
欢迎加入:Java、C++、Python技术交流QQ群(982860385),大佬免费带队,有问必答
欢迎加入:C++ GESP/CSP认证学习QQ频道,考试资源总结汇总
欢迎加入:C++ GESP/CSP学习交流QQ群(688906745),考试认证学员交流,互帮互助
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。