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;
}