洛谷: P1102 A-B 数对

洛谷: P1102 A-B 数对

原题链接

P1102 A-B 数对

题目背景

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

题目描述

给出一串正整数数列以及一个正整数 C C C,要求计算出所有满足 A − B = C A - B = C AB=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个正整数 N , C N,C N,C

第二行, N N N 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 A − B = C A - B = C AB=C 的数对的个数。

样例 #1

样例输入 #1

4 1
1 1 2 3

样例输出 #1

3

提示

对于 75 % 75\% 75% 的数据, 1 ≤ N ≤ 2000 1 \leq N \leq 2000 1N2000

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1 \leq N \leq 2 \times 10^5 1N2×105 0 ≤ a i < 2 30 0 \leq a_i <2^{30} 0ai<230 1 ≤ C < 2 30 1 \leq C < 2^{30} 1C<230

2017/4/29 新添数据两组

题目解读

给定一个数 C C C, 在 n n n个数中求出所有满足 A − B = C A-B=C AB=C ( A , B ) (A, B) (A,B)数对的个数

算法实现

方法一 双指针法

先用 s o r t sort sort升序排序, 对于每一个 a [ i ] a[i] a[i], 用双指针找到等于 A − C A-C AC的区间,假设为 a [ l ] 到 a [ r ] ( l < r ) a[l]到a[r](l<r) a[l]a[r](l<r), 则 a [ l ] 到 a [ r ] a[l]到a[r] a[l]a[r]的每一个数(包括 a [ l ] 但不包括 a [ r ] a[l]但不包括a[r] a[l]但不包括a[r])都等于 a [ i ] − C a[i]-C a[i]C, 用 a n s ans ans累加出每一个 a [ i ] a[i] a[i]对应的 a [ l ] 到 a [ r ] a[l]到a[r] a[l]a[r]的个数即可, 即 a n s + = r − l ans+=r-l ans+=rl.

至于如何去找到 B = A − C B=A-C B=AC中的 B B B, 首先将两个指针 l , r l,r l,r都指向第一个数字的下标(本题中是0,如果数组从1开始就是1), 只要 B < A − C B<A-C B<AC s [ l ] < s [ i ] − c s[l]<s[i]-c s[l]<s[i]c,且满足 l < n l<n l<n的情况下, l l l指针向右移动, l + + l++ l++, 同理我们用类似的方法确定 r r r, 但为了保证r是相同连续区间 B B B的右端点(不包括 r r r),则应该 B ≤ A − C B \le A-C BAC s [ r ] ≤ s [ i ] − c s[r] \le s[i]-c s[r]s[i]c.

最后, 找到了相同的连续区间的起始值, 检验一下 s [ l ] = = s [ i ] − c s[l]==s[i]-c s[l]==s[i]c, 如果成立,则对于 A = s [ i ] A=s[i] A=s[i]来说, 这样的 B B B存在, 用 a n s ans ans累加求和即可; 如果不成立, 则不存在这样的 B B B, 继续找下一个 s [ i ] s[i] s[i]即可, 直到遍历完所有的 s [ i ] s[i] s[i].

方法二 二分法

在排好序的情况下, 函数 u p p e r _ b o u n d ( a + 1 , a + n + 1 , a [ i ] + c ) upper\_bound(a+1,a+n+1,a[i]+c) upper_bound(a+1,a+n+1,a[i]+c)能快速返回第一个大于 a [ i ] + c a[i]+c a[i]+c的地址, l o w e r _ b o u n d ( a + 1 , a + n + 1 , a [ i ] + c ) lower\_bound(a+1,a+n+1,a[i]+c) lower_bound(a+1,a+n+1,a[i]+c)能快速返回第一个大于或者等于 a [ i ] + c a[i]+c a[i]+c的地址, 两个地址相减即可求出有多少个相同的 a [ i ] + c a[i]+c a[i]+c.最后方法同上, 遍历数组累加求和即可.

方法三 STL map

m a p map map容器记录下不同数字出现的次数, 数字的值为 m a p map map的下标, 数字的个数为 m a p map map对应下标的值,
再遍历以这 n n n个数减去 C C C的值为下标的 m a p map map的值, 并求和, 最后输出这个和即可

AC代码

双指针法一

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int s[N];
int main(){
	int n,c;
	cin>>n>>c;
	for(int i=0;i<n;i++)cin>>s[i];//先输入在排序
	sort(s,s+n);
	int l=0,r=0;
	long long ans=0;
	for(int i=0;i<n;i++){
		while( (s[l]<s[i]-c)&&l<n)l++;//找连续相同的B的最左边的那个数的坐标
		while( (s[r]<=s[i]-c)&&r<n)r++;//找到连续的B的最右边的那个数的坐标
		if(s[i]-c==s[l])ans+=r-l;
	}
	cout<<ans<<endl;
	return 0;
}

双指针法二

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N],c,n;
int main(){
	cin>>n>>c;
	long long ans=0;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);//先排序
	for(int i=1,j=1,k=1;i<=n;i++){
		while(j<=n&&a[j]-a[i]<c)j++;//指针后移,找数字相同的区间
		while(k<=n&&a[k]-a[i]<=c)k++;//指针后移,此时[j,k]内的数字是相同的
		if(a[j]-a[i]==c&&a[k-1]-a[i]==c&&k-1>=1)ans+=k-j;
	}
	cout<<ans<<endl;
	return 0;
}

二分法

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N],n,c;
long long s=0;
int main(){
	cin>>n>>c;
	for(int i=1;i<=n;i++)cin>>a[i];
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
		s+=upper_bound(a+1,a+n+1,a[i]+c)-lower_bound(a+1,a+n+1,a[i]+c);
		//s+=upper_bound(a+1,a+n+1,a[i]-c)-lower_bound(a+1,a+n+1,a[i]-c);//等价于上一句
	cout<<s<<endl;
	return 0;
}

STL map

#include <bits/stdc++.h>
//#include <map>  //map的头文件
using namespace std;
const int N=2e5+10;
map<int,int>ds;
int a[N],n,c;
long long ans=0;
int main()
{                   
	cin>>n>>c;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		ds[a[i]]++;
	}
	for(int i=1;i<=n;i++)
		ans+=ds[a[i]-c];
	cout<<ans<<endl;
	return 0;
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值