寒假每日一题之 A-B问题

本文介绍了如何利用map数据结构和二分查找解决洛谷网站上的一个计算机科学问题,即计算满足A-B=C的数对数量。作者提供了两种方法,一种是使用map存储每个数的出现次数,另一种是利用二分查找的lower_bound和upper_bound函数优化求解过程,时间复杂度为O(NlogN)。
摘要由CSDN通过智能技术生成

P1102 A-B 数对 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目背景

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 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,0≤ai​<230,1≤C<230。

copy一下

解法1:

map容器

大致思路:

求满足A-B=C的所有种类   等价于A-C=B的所有种类

相求出来满足条件的B的所有情况,然后在将存在该情况的次数加起来

解决方案

先用map数据结构(相关知识可以参照往期文章),来储存每一个正整数出现的次数,即   map<int int >,第一个int代指a[i],即正整数,第二个int代指count,统计各个数出现的次数,然后再将满足上式的B表示出来,最后找存在B情况的和

#include<iostream>
#include<map>
using namespace std;
typedef long long ll;
ll a[1000000];
int main (){
	map<ll,ll>cnt;
	int n,c;
	cin>>n>>c;
	for(int i=0;i<n;i++){
		cin>>a[i];
		cnt[a[i]]++;
		a[i]-=c;	
	}
	ll ans=0; 
	for(int i=0;i<n;i++)
	ans+=cnt[a[i]];
	cout<<ans;
	
	return 0;
}

解法2:

  这道题有一个用二分的神奇做法。

Q1:什么是lower_bound/upper_bound?

这两个函数是STL中用于二分查找的两个函数,用法: 假定我们有一个有序的数组a,并将数x作为二分查找的目标,那么:

lower_bound(a,a+n,x)-a      //下标从0开始
lower_bound(a+1,a+n+1,x)-a  //下标从1开始

它们就能取得最小的a数组的下标i,满足ai​⩾x。

upper_bound(a,a+n,x)-a      //下标从0开始
upper_bound(a+1,a+n+1,x)-a  //下标从1开始

它们就能取得最小的a数组的下标i,满足ai​>x。


那他如何来解这道题呢?

我们发现,如果这个数组是有序的,那么对于每一个A的值,在它的后方就只有一个数值B满足A,B的差值为C。

这时我们只需要使用两个函数求出数组中对于每个A,A+C的这两个位置,它们的差即为数组中数值为A+C的元素个数。将这个数加到ans中。

for(int i=1;i<=N;i++)
    {
        ans+=((upper_bound(a+1,a+N+1,a[i]+C)-a)-(lower_bound(a+1,a+N+1,a[i]+C)-a));
    }

同时这样也有效避免了重复,因为只向后方查找。

输入的数据是无序的,我们只需要运算之前一遍sort即可。

总复杂度是O(NlogN)的。(两个函数的复杂度为logN)

最后贴上AC代码(没开O2)

#include<bits/stdc++.h>
using namespace std;
long a[200001];
long N,C,ans;
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++)
    {
        ans+=((upper_bound(a+1,a+N+1,a[i]+C)-a)-(lower_bound(a+1,a+N+1,a[i]+C)-a));
    }
    cout<<ans;
    return 0;
}

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Zzcat.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值