原题链接:P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1102
题目描述:
给出一串长度为N的正整数数列以及一个正整数 C,要求计算出所有满足A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)
输入格式:
输入共两行。
第一行,两个正整数N,C。第二行,N 个正整数,作为要求处理的那串数。
输出格式:
满足A−B=C 的数对的个数。
数据限制:
对于 75%的数据,1≤N≤2000。
对于 100%的数据,1≤N≤2×10^5,0≤ai<2^30,1≤C<2^30。
解题思路:
暴力解法当然是在数组中遍历查找满足条件的A和B,时间复杂度为O(N^2),可以得到75%的分数。但对于剩下25%的测试点显然是通过不了的。
既然是查找问题,自然可以通过二分搜索进行优化。思路就是:先对数组进行应用快速排序,将其变为有序的序列,然后在寻找满足A-B=C的所有数对个数时,遍历所有A值,通过改进的二分查找满足条件的B值。此外,由于题目要求在原数组中不同位置的数字一样的数对算不同的数对,即查找到的满足条件的B有多少个相同的值,那么数对的总数也要加上多少个。所以使用一个map来存储某个数值在输入数组中对应有多少个相同的值
改进的二分查找函数体如下(数列已提前从小到大排列好):
map<long long, long long>m;
void binary(int left,int right,int num) {
if (left > right) return;
int mid = (left + right) / 2;
if (num - a[mid] == c) {
//加上这个数重复出现了多少次
cnt += m[a[mid]];
return;
}
//如果差值比C大,应该往右找
else if (num - a[mid] > c) binary(mid + 1, right, num);
//如果差值比C小,应该往左找
else binary(left, mid - 1, num);
}
一、参数含义:left和right代表当前二分搜索的左右边界。nums代表当前的A值
二、累加条件:如果num-a[mid]==c(即A−B=C)时,表明当前的a[mid]满足条件,那么总数目则累加上该满足条件的与B相同值的个数
(tips:如果题目改为,不同位置的数字一样的数对算相同的数对,此时只需要cnt++即可)
三、递归方向:
1. 如果num-a[mid]>c,则说明当前的a[mid]还太小,应该往数值更大的方向查找,此处应笔者提前进行了从小到大排序,故应向右继续二分查找。
2.同理,如果num-a[mid]<c,则说明当前的a[mid]太大,应该往数值更小的方向查找。
(无论C的值正或负,递归查找方向都如上)
代码:
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
long long a[200001],n,c;
long long cnt;
map<long long, long long>m;
void binary(int left,int right,int num) {
if (left > right) return;
int mid = (left + right) / 2;
if (num - a[mid] == c) {
//加上这个数重复出现了多少次
cnt += m[a[mid]];
//cout << num << " " << a[mid]<<" "<<cnt<<endl;
return;
}
//如果差值比C大,应该往右找
else if (num - a[mid] > c) binary(mid + 1, right, num);
//如果差值比C小,应该往左找
else binary(left, mid - 1, num);
}
//思路:寻找满足A=B=C的所有数对个数,遍历所有A值,通过二分查找满足条件的B值
int main() {
cin >> n >> c;
for (int i = 0; i < n; i++) {
cin >> a[i];
m[a[i]]++;
}
sort(a, a + n);
//固定被减数,二分查找减数
for (int i = 0; i < n; i++) {
binary(0, n - 1, a[i]);
}
cout << cnt;
return 0;
}