题目 链接
P1102 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 ≤ N ≤ 2000。
对于 100% 的数据,1 ≤ N ≤ 2×105。
保证所有输入数据都在 32 位带符号整数范围内。
题目简析
给定C求A - B 等于C的数对,时间复杂度要求是O(nlogn),原先是两个循环扫描数对就能搞定,但是由于时间限制,我们需要尝试优化其中一个循环。
所以我们先对所有数字排序,然后用一个循环去枚举 A ,枚举A之后呢,就能算出 B = A - C,然后我们只需要知道B的个数即可。可以通过桶排序进行索引,但是数据范围过大比较麻烦 ,也可以用离散化或者直接红黑树统计数量啦。但是这里我们用二分,这是一个很经典的整数二分的题目,排序结束后,找到一个数字的起始位置l和终止位置r,那么数字个数就是len = r - l + 1,如果没有找到得到的数量应该是0。然后再把所有的数字加起来即可。
参考代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2E5 + 10;
int a[N];
int n,k;
int solve1(int x) {
int l = 1, r = n;
while (l < r) {
int mid = l + r >> 1;
if (a[mid] >= x) r = mid;
else l = mid + 1;
}
if (a[l] != x)return -1;
return l;
}
int solve2(int x) {
int l = 1, r = n;
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] <= x) l = mid;
else r = mid - 1;
}
if (a[l] != x) return -1;
return l;
}
int main() {
// freopen("in.txt", "r", stdin);
scanf("%d%d", &n,&k);
for (int i = 1; i <= n; i++) scanf("%d", a + i);
sort(a + 1, a + 1 + n);
long long cnt = 0;
for (int i = 1; i <= n; i++) {
int x = a[i] - k;
if (x < 1) continue;
int l = solve1(x);
if (l == -1) continue;
int r = solve2(x);
int len = r - l + 1;
cnt += len;
}
printf("%lld\n", cnt);
return 0;
}
备注
整数二分实际上只有两个板子,就是合法边界的最左边和最右边。