[luogu p1102] A-B 数对
A-B 数对
题目描述
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!
好吧,题目是这样的:给出一串数以及一个数字 \(C\),要求计算出所有 \(A - B = C\) 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入输出格式
输入格式
输入共两行。
第一行,两个整数 \(N, C\)。
第二行,\(N\) 个整数,作为要求处理的那串数。
输出格式
一行,表示该串数中包含的满足 \(A - B = C\) 的数对的个数。
输入输出样例
输入样例 #1
4 1
1 1 2 3
输出样例 #1
3
说明
对于 \(75\%\) 的数据,\(1 \leq N \leq 2000\)。
对于 \(100\%\) 的数据,\(1 \leq N \leq 2 \times 10^5\)。
保证所有输入数据都在 \(32\) 位带符号整数范围内。
2017/4/29 新添数据两组
分析
这道题是我在二分题单里遇到的,但是这道题的思路我死活想不到二分……
我的思路是这样的,首先把数排序一遍,然后如果A和C都确定了,那么B也只能是那个数。题目中说
不同位置的数字一样的数对算不同的数对
所以每一次扫到一个A,后面每一个B都是一种方案,也就是说方案数多了 B的个数 种。
思路也就很明显了,
- 排序一遍数字
- 从前往后扫,把这个数当成A
- 通过A和C算出B,查找序列中B的个数
- 方案数加上B的个数
思考到这里我终于明白为什么这道题在二分题单里了。我们来看最后一条:
方案数加上B的个数
这个B的个数怎么求呢?自然有一种方案是从头到尾扫一遍,不过这样的复杂度是\(\operatorname{O}(N)\),外层循环也是\(\operatorname{O}(N)\),整体的复杂度就是\(\operatorname{O}(N^2)\)了。然而数据范围是\(1 \le N \le 2 \times 10^5\)。\(\operatorname{O}(N^2)\)的复杂度显然不行。
那怎么办呢?
这个时候二分就要出场了。
不要忘记了数是排序的,也就是说数字为B的数在这个序列中是连续的。那么我们只要算出这个序列的头和尾就能算出数字为B的个数。
算出一个连续为B序列的头和尾,这就是裸到不能再裸的二分了。
那么是手写二分还是STL呢?这里就根据你的习惯来了。这里我选择STL,也就是lower_bound和upper_bound。
思路理明白,代码就简单了。
代码
/*
* @Author: crab-in-the-northeast
* @Date: 2020-06-13 10:29:10
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-06-13 10:53:14
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
inline long read() {
long x;
std :: cin >> x;
return x;
}
const int maxn = 200005;
long a[maxn];
int main() {
long n = read(), c = read(), ans = 0;
for (int i = 1; i <= n; ++i) a[i] = read();
std :: sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i)
ans += (std :: upper_bound(a + 1, a + n + 1, a[i] + c) - a) - (std :: lower_bound(a + 1, a + n + 1, a[i] + c) - a);
std :: cout << ans << std :: endl;
return 0;
}