题目描述
楠楠在网上刷题,感觉第一题:求两数的和(A+B Problem)太无聊了,于是增加了一题:A-B Problem,难倒了一群小朋友,哈哈。 //楠楠真的欠(╬▔皿▔)
题目是这样的:给出N 个从小到大排好序的整数,一个差值C,要求在这N个整数中找两个数A 和B,使得A-B=C,问这样的方案有多少种?
例如:N=5,C=2,5 个整数是:2 2 4 8 10。答案是3。具体方案:第3 个数减第1 个数;第3 个数减第2个数;第5 个数减第4 个数。
输入
从文件 dec.in
中读入数据。
第一行2 个正整数:N,C。
第二行N 个整数:已经有序。注意:可能有相同的。
输出
输出到文件 dec.out
中。
一个整数,表示该串数中包含的所有满足A-B=C 的数对的方案数。
样例输入
4 1 1 1 2 2
样例输出
4
数据范围限制
5 个数据:N 的范围是[1…1,000]。
5 个数据:N 的范围是[1…100,000]。
所有数据:
C 的范围是[1…1,000,000,000]。
N 个整数中每个数的范围是:[0…1,000,000,000]。
这道题眨眼一看,双重循环秒了 或 用个桶就行了
但一看数据范围,直接炸了
每个数的范围[1~1000000000],那N也的范围也是[1~1000000000],甚至更大
双重循环会时间超限,桶会空间超限。
于是我得用一种 与众不同 的方法
我们来看一下题目的样例
5 2 //5个数 差值为2
2 2 4 8 10
我们可以定义一个 i 和 j 枚举每一个数(j>i),题目上说输入一定是个从小到大的数列,那我们只需要判断:第 j 个数 - 第 i 个数是否为 C (C为两个数的差值,见题目),是的话就 ans+1,不是的话 j+1 到下一个数。
见下表
2 | 2 | 4 | 8 | 10 |
i | j |
第 j 个数 2 - 第 i 个数 2 = 0 ,差值不对,找下一个数, j+1
2 | 2 | 4 | 8 | 10 |
i | j |
第 j 个数 4 - 第 i 个数 2 = 2 ,差值相同,j 不变,i+1 ,ans+1
2 | 2 | 4 | 8 | 10 |
i | j |
第 j 个数 4 - 第 i 个数 2 = 2 ,差值相同,j 不变,i+1 ,ans+1
2 | 2 | 4 | 8 | 10 |
i j |
第 j 个数 4 - 第 i 个数 4 = 0 ,差值不对,找下一个数, j+1
2 | 2 | 4 | 8 | 10 |
i | j |
第 j 个数 8 - 第 i 个数 4 = 4 ,差值不对且差值大于C,找下一个数, i+1,j+1
当两数差值大于 C 时,j 再 +1 只会让差值更大,所以直接跳过,i+1(这是一个从小到大的数列)
2 | 2 | 4 | 8 | 10 |
i | j |
第 j 个数 10 - 第 i 个数 8 = 2 ,差值相同,j 不变,i+1,ans+1
2 | 2 | 4 | 8 | 10 |
i j |
第 j 个数 10 - 第 i 个数 10 = 0 ,差值不对,结束
所以总结一下 差值相同i+1 差值不同j+1(若差值比C大 i+1)
理论来说,我们只用这样
for(long long i=1;i<=cnt;i++) //i+1 枚举 时间复杂度为 O(cnt)
{
for(long long j=s;j<=cnt;j++) //从s开始 见第22行
{
if(t[j]-t[i]==c) //两数差值为 C
{
ans++; //ans+1
s=j+1; //j+1 用s记录
break;
}
if(t[j]>t[i]+c) break; //两数差值大于 C 直接退出
}
}
但我们似乎少算了一个问题
当N为11 C为5 数列为 3 3 3 8 8 8 8 8 8 9 9 时
我们代入总结的方法,j 只会到第一个8然后就不会向后,直到 i 也到了8 j 才向后,这样就会漏下很多的正确结果
因此我们得把它们装进桶里面达到去重,达咩 桶不是会爆吗?
但我这个桶不是一般的桶(存下标的),而是王维诗里自己创建的按顺序的桶,LOOK
我们来存一下样例
下标 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
数字 | 3 | 8 | 9 | ||||||
数量 | 3 | 6 | 2 |
像这样,既能做到去重,又能保存数量,而且节约空间。让我们把装进桶的数列代入总结的方法,
数字 | 3 | 8 | 9 |
数量 | 3 | 6 | 2 |
ans=0 | i | j |
第 j 个数 8 - 第 i 个数 3 = 5 ,差值相同,j 不变,i+1,ans+6*3(差值相同的情况下,有6个8,3个3,差值相同就有6*3=18个,想一想,是不是很妙)
数字 | 3 | 8 | 9 |
数量 | 3 | 6 | 2 |
ans=18 | i j |
第 j 个数 8 - 第 i 个数 8 = 0 ,差值不对,j+1
数字 | 3 | 8 | 9 |
数量 | 3 | 6 | 2 |
ans=18 | i | j |
第 j 个数 10 - 第 i 个数 10 = 0 ,差值不对,结束
代码实现,见下
#include<bits/stdc++.h>
using namespace std;
long long n,c,a,b,t[100001],sum[100001],ans,cnt,s;
//定义数列长度n 差值c 存数字的t 存数量的sum 结果ans
int main()
{
cin>>n>>c; //输入 n 和 数的差值
for(long long i=1;i<=n;i++) //人工桶
{
cin>>a;
if(a!=b || i==1) ++cnt; //前后两数是否相同 不是就要把新的数入桶 见第14行
t[cnt]=a; //入桶 t表示数字
sum[cnt]++; //统计数量 sum表示数量
b=a; //保存上一个数,用于跟新的数比较
}
for(long long i=1;i<=cnt;i++) //i+1 枚举 时间复杂度为 O(cnt)
{
for(long long j=s;j<=cnt;j++) //从s开始 见第22行
{
if(t[j]-t[i]==c) //两数差值为 C
{
ans+=sum[i]*sum[j]; //ans+数量*数量
s=j+1; //j+1 用s记录
break;
}
if(t[j]>t[i]+c) break; //两数差值大于 C 直接退出让i操作
}
}
cout<<ans;
return 0;
}
完结撒花!!!!!!!!!!!!!!!!!!!