洛谷 P1102 A-B 数对 C++ 二分法 (排序,去重)

题目描述

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 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;
}

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Prudento

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值