A-B 数对

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;
}
posted @ 2023-04-01 22:24  HEIMOFA  阅读(340)  评论(0)    收藏  举报