A-B 数对
题目描述
给出一串正整数数列以及一个正整数 \(C\),要求计算出所有满足 \(A - B = C\) 的数对的个数(不同位置的数字一样的数对算不同的数对)。
输入格式
输入共两行。
第一行,两个正整数 \(N,C\)。
第二行,\(N\) 个正整数,作为要求处理的那串数。
输出格式
一行,表示该串正整数中包含的满足 \(A - B = C\) 的数对的个数。
样例输入
4 1
1 1 2 3
样例输出
3
提示
对于 \(100\%\) 的数据,\(1 \leq N \leq 2 \times 10^5\),\(0 \leq a_i <2^{30}\),\(1 \leq C < 2^{30}\)。
很明显这是一道基础的二分查找题目
关于二分查找有两个常用模板:
int l_find(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;
}
return l;
}
int r_find(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;
}
return r;
}
第一个可以在数组里尽可能向左找,第二个可以在数组里尽可能向右找
由题得:\(A=B+C\) 对于 \(B\) 来说,我们可以枚举每一个数组里的数,然后查找数组里第一个出现 \(A\) 的位置 ,如果有的话,那么再查找 \(A\) 最后所出现的位置,得到长度后用一个 \(ans\) 维护答案即可
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
int n,C;
long long ans;//如果有极限数据,比如:十万个1与十万个2,C=1,则答案为10000000000,超出int范围,要用long long
const int N=200005;
int a[N];
int l_find(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;
}
return l;
}
int r_find(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;
}
return r;
}
int main()
{
scanf("%d%d",&n,&C);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+n+1);//由于要二分查找,所以必须先排序
for(int i=1;i<=n;i++){
int A=C+a[i];
if(a[l_find(A)]==A){
ans+=r_find(A)-l_find(A)+1;
}
}
printf("%lld",ans);
return 0;
}
这道题亦可用双指针来做
用 \(i\) 来当左指针,\(j\) 来当右指针,只要 \(a_j-a_i\) 依旧小于 \(C\) 那么 \(j\) 指针往右移
但是这道题会有重复数据,所以需要把 \(j\) 换成 \(j1\) 与 \(j2\) 分别维护 \(j\) 的右端点与左端点减一(这里会有个减一是因为判断时会找到最后一个小于 \(C\) 的数,所以会减一)
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
int n,C;
long long ans;
const int N=200005;
int a[N];
int main()
{
scanf("%d%d",&n,&C);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
sort(a+1,a+1+n);//双指针也需要排序
int j1=1,j2=1;//j1:连续区间中的右端点,j2:连续区间中的左端点-1
for(int i=1;i<=n;i++){
while(j1<=n&&a[j1]-a[i]<=C) j1++;
while(j2<=n&&a[j2]-a[i]<C) j2++;
ans+=j1-j2;
}
printf("%lld",ans);
return 0;
}
这道题还可以用 \(map\) ,建立一个 \(mp_i=j\) 表示在数组里, \(i\) 出现的次数是 \(j\) 次
再将 \(A-B=C\) 转化为 \(A-C=B\) 即可
这个方法也是我认为效率最高的了
(我们有个习惯,就是类似需要把数据当作下标(也就是需要装桶)的题目,都使用 \(map\) ,尽管数据并不需要)
\(Code:\)
#include<bits/stdc++.h>
using namespace std;
int n,C;
long long ans;
const int N=200005;
int a[N];
map<int,int> mp;
int main()
{
scanf("%d%d",&n,&C);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
mp[a[i]]++;//统计每个数字的出现次数
a[i]-=C;//将A全部转化成B
}
for(int i=1;i<=n;i++) ans+=mp[a[i]];//统计每个B所出现的次数即可
printf("%lld",ans);
return 0;
}