2025 - 02 - 13 - 第 52 篇
作者(Author): 郑龙浩 / 仟墨(CSND)
【二分搜索】
洛谷P1102 A-B 数对
题目背景
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
题目描述
给出一串正整数数列以及一个正整数 C C C,要求计算出所有满足 A − B = C A - B = C A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 N , C N,C N,C。
第二行, N N N 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 A − B = C A - B = C A−B=C 的数对的个数。
输入输出样例 #1
输入 #1
4 1
1 1 2 3
输出 #1
3
说明/提示
对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1≤N≤2000。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1≤N≤2×105, 0 ≤ a i < 2 30 0 \leq a_i <2^{30} 0≤ai<230, 1 ≤ C < 2 30 1 \leq C < 2^{30} 1≤C<230。
2017/4/29 新添数据两组、
思路
**DeepSeek 根据我代码,整理的思路:**懒得写思路整理了
好的!下面我将从 问题分析、代码思路、实现细节 和 示例运行 四个方面,清晰地解释这个代码的逻辑,帮助你更好地理解和记笔记。
1. 题目分析
- 直接暴力枚举所有数对 A A A 和 B B B,检查是否满足 A − B = C A - B = C A−B=C,时间复杂度为 O ( N 2 ) O(N^2) O(N2),对于 N = 2 × 1 0 5 N = 2 \times 10^5 N=2×105 的数据规模来说,这种方法会超时。
- 需要一种更高效的方法来解决这个问题。
2. 代码思路
2.1 转化问题
将 A − B = C A - B = C A−B=C 转化为 A = B + C A = B + C A=B+C,即在数组中寻找满足 A = B + C A = B + C A=B+C 的元素对。
2.2 核心思想
- 对于数组中的每一个元素 B B B,计算 A = B + C A = B + C A=B+C。
- 统计数组中等于 A A A 的元素个数,这些元素都可以与 B B B 组成满足 A − B = C A - B = C A−B=C 的数对。
2.3 实现方法
- 排序:对数组进行排序,以便使用二分查找。
- 二分查找:
- 使用
lower_bound
找到第一个等于 A A A 的位置。- 使用
upper_bound
找到第一个大于 A A A 的位置。- 两者之间的差值就是等于 A A A 的元素个数。
- 累加结果:将所有满足条件的数对个数累加到
ans
中。
3. 代码实现细节
3.1 输入处理
long long N, C; // 数组长度 和 差值 C cin >> N >> C; // 输入数组长度和差值 C vector<long long> arr(N); // 存储输入的所有数字 for (auto i = arr.begin(); i < arr.end(); i++) { cin >> *i; // 输入数组元素 }
- 使用
vector<long long>
存储数组元素,支持动态大小。- 通过迭代器遍历数组并读取元素。
3.2 排序
sort(arr.begin(), arr.end()); // 对数组进行排序
- 对数组进行升序排序,以便后续使用二分查找。
3.3 遍历数组并统计数对
long long ans = 0; // 存放满足 A - B = C 的数量 for (auto i = arr.begin(); i < arr.end(); i++) { auto first_space = lower_bound(arr.begin(), arr.end(), *i + C); auto last_space = upper_bound(arr.begin(), arr.end(), *i + C); ans += last_space - first_space; }
lower_bound
:找到第一个大于或等于 A = B + C A = B + C A=B+C 的位置。upper_bound
:找到第一个大于 A = B + C A = B + C A=B+C 的位置。last_space - first_space
:计算等于 A A A 的元素个数。- 将满足条件的数对个数累加到
ans
中。3.4 输出结果
cout << ans; // 输出结果
- 输出最终的数对个数。
4. 总结
1核心思想
- 将 A − B = C A - B = C A−B=C 转化为 A = B + C A = B + C A=B+C,通过排序和二分查找高效解决问题。
2时间复杂度
- 排序: O ( N log N ) O(N \log N) O(NlogN)。
- 遍历数组 + 二分查找: O ( N log N ) O(N \log N) O(NlogN)。
- 总时间复杂度: O ( N log N ) O(N \log N) O(NlogN)。
3空间复杂度
- O ( N ) O(N) O(N),用于存储数组。
代码
// 洛谷P1102 A - B 数对
// 2025-02-12
// 郑龙浩 / 仟濹
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
using namespace std;
int main( void ){
long long N, C; // 数组长度 差(被减数 - 减数 结果)
cin >> N >> C; // 输入数组长度
vector <long long> arr( N ); // 存储输入的所有数字
for ( auto i = arr.begin(); i < arr.end(); i ++ ){
cin >> *i;
}
sort ( arr.begin(), arr.end() );
long long ans = 0; // 存放满足 A - B = C 的数量
// 反过来判断,计算 B + C == A 的数量,也就是寻找A的数量
for ( auto i = arr.begin(); i < arr.end(); i ++ ){
// first_space - 第一个 B + C
// last_space - 最后一个 B + C
// ans --> A 的数量 == B + C == A 的数量 == A - B 的数量
auto first_space = lower_bound( arr.begin(), arr.end(), *i + C );
auto last_space = upper_bound( arr.begin(), arr.end(), *i + C );
ans += last_space - first_space;
}
cout << ans;
return 0;
}