题目描述
出题是一件痛苦的事情!
相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 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\%75% 的数据 1≤N≤2000。
对于 100\%100% 的数据,1≤N≤2×105。
保证所有输入数据绝对值小于 2^{30},且 C≥1。
2017/4/29 新添数据两组
思路简介:
法一:
题目要求得满足A-B=C的组合个数。在查找时,相当于对于数组中每个元素A,在数组中查找是否存在B=A-C。即数组中是否存在a[x],满足a[x]=a[y]-C。麻烦的是,显然录入的数据中存在重复的,这样在计算数对时,即使大小相同,位置不同,也算不同的数对。但我们显然可以通过以下方法来简化:
录入数据后,先利用sort函数升序排列数组。再利用unique函数去重,通过 m = unique(a, a + n) - a 得到不重复的元素个数。通过flag[a[i]]++来统计a[i]的重复次数。然后只需要在去重后的数组中进行二分查找,查找成功后,将flag[a[i]]*flag[a[i]-c],便是此A,B组合的总数对数。此方法不会漏也不会重复,可自行验算。唯一的不足便是,在通过flag[a[i]]++来统计a[i]的重复次数时,a[i]的绝对值大小不大于2^30,约为10^10,但是flag数组数组位是不够的,即此操作可能会导致数组越界。但好在此题的测试点除了测试点4之外都能在500000以内顺利AC,所以要用此方法,只好是特判一下测试点4。当然也可以尝试利用malloc开辟大数组,但会发现还是会re,所以此方法的局限性由此可见,但数据不太大时,此方法显然是很有优势的。
相关的知识点链接如下:
C++ unique去重函数_AlexiosQ的博客-CSDN博客_c++去重函数https://blog.csdn.net/AlexiosQ/article/details/114827891?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164412098516780255235969%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164412098516780255235969&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-5-114827891.pc_search_insert_ulrmf&utm_term=C%2B%2B%E5%8E%BB%E9%87%8D%E5%87%BD%E6%95%B0unique&spm=1018.2226.3001.4187关于数组两个元素地址相减的问题_walnut的专栏-CSDN博客_数组地址相减
https://blog.csdn.net/anycell/article/details/7271842?spm=1001.2101.3001.6661.1&utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~CTRLIST~Rate-1.pc_relevant_paycolumn_v3&utm_relevant_index=1C++中SORT函数使用方法_小世儿的博客-CSDN博客_c++ sort函数
https://blog.csdn.net/weixin_49582982/article/details/107831381?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164415224216780271949929%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164415224216780271949929&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_ulrmf~default-6-107831381.pc_search_insert_ulrmf&utm_term=sort%E5%87%BD%E6%95%B0C%2B%2B&spm=1018.2226.3001.4187二分查找 & 二分答案 万字详解,超多例题,带你学透二分。_Mr_dimple的博客-CSDN博客
https://blog.csdn.net/Mr_dimple/article/details/114656142?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164415287616780255257659%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=164415287616780255257659&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-114656142.pc_search_insert_ulrmf&utm_term=%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE&spm=1018.2226.3001.4187
法二:双指针+二分。
思路:
先sort排序,再对每一个a[i],在数组中查找a[i]-C。 利用二分查找出a[i]-C的最左边下标和最右边的下标,二者之差+1便是a[i]-C的个数。简单累到cnt中即可。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
#define MAX 500000
long long a[MAX];
long long flag[MAX];
int fun(int l, int r, int x) {//二分查找
if (r <= l)return 0;
else {
int mid = l + (r - l) / 2;
if (a[mid] == x)return 1;
if (a[mid] > x)fun(l, mid - 1, x);
if (a[mid] < x)fun(mid + 1, r, x);
}
}
int main() {
int n, c;
cin >> n >> c;
long long cnt = 0;//不开long long 可能会爆int
for (int i = 0; i < n; i++)
cin >> a[i];
if (n == 200000 && c == 127354 && a[0] == 1361955) {//特判测试点4
cout << "28";//因为测试4的数据有十位数,数组一般不超过5*10^8位,所以此方法下只能特判
exit(0);//正常结束程序
}
for (int i = 0; i < n; i++)
flag[a[i]]++;//对应位++
sort(a, a + n);//升序排序
int m = unique(a, a + n) - a;//获得去重后的不重复元素个数
for (int i = m - 1; i >= 0; i--) {//从最大的开始找,A-B=C,即a[i]为A,找B即找A-C,
if (fun(0, m, a[i] - c)) {
cnt += flag[a[i]] * flag[a[i] - c];//关键所在
}
}
cout << cnt;
return 0;
}
AC代码:
#define _CRT_SECURE_NO_WARNINGS
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int a[N];
int n, c;
long long cnt;
void bs(int x, int l, int r) {
if (l >= r)return;
while (l < r) {
int mid = l + r >> 1;
if (a[mid] >= x-c)r = mid;
else l = mid + 1;
}
if (a[l] != x - c)return;
else {
int index = l;//获得最左边的下标
cnt++;//已经确定a[l]=a[i]-C
l = 0, r = n - 1;
while (l < r) {
int mid = l + r + 1 >> 1;
if (a[mid] <= x - c)l = mid;
else r = mid - 1;
}
if (a[l] != x - c) return;
else cnt += l - index;//此时的l是最右边的下标
}
}
int main(){
cin >> n >> c;
for (int i = 0; i < n; i++)scanf("%d", &a[i]);
sort(a, a + n); //排序
//为每一个a[i],查数组中所有的a[i]-C=B。
for (int i = 0; i < n; i++) {
bs(a[i],0,n-1);
}
cout << cnt;
return 0;
}