题目
分析
要解决这道题,需要掌握双指针这个编程思路。
我们来看样例输入,逐步分析如何用双指针法来解题:
输入1:8 3
输入2:2 5 3 7 5 2 4 8
如果按照输入的顺序,从左往右一个一个遍历,那么复杂度会是\(O(N^2)\)。
我们首先要想到的,是排序这个输入,这是一个\(O(nlog n)\)的操作。得到:
2 2 3 4 5 5 7 8
其次,我们应该想得到:排序后,如果随机挑两个数进行A(右数)-B(左数)的操作,其差可以大于、等于、小于3(也就是输入的c)。
进行一次左到右的遍历(\(O(n)\)),对于每个当前选中的s[i],我们用两根“针”(l和r)来确定:这个区间里的数字是否满足s[r]-s[l]=C:
s[i]-s[l]>C,那么左边界太低了,l++ && l <= n。l一直增加,到某个点,s[i]-s[l]==c。s[r]-s[i]<=C,那么右边界太低了,r++ && r <= n。r一直增加,到某个点,s[i]-s[l]>c。
此时,[l, r-1]这个区间内一定都是s[i]-C的值,也就是题目中要求的计数:\(count = (r-1)-l+1 = r-l\)。
由于我们是从小到大遍历排序好的数字,所以s[i]越来越大,s[i]-C也越来越大,l/r会不断向右,不会回头。
当然,一开始的时候,这两个“指针”要从0开始。
答案

思考
双指针是很重要的一个概念。请读者认真研究并掌握。
这道题在“集合”这个章节再次出现,用STL后,会更简单、更直观地解决。代码如下:
#include <bits/stdc++.h>
using namespace std;
const int MAX=200'010;
map<int, int> s;
int a[MAX];
int n, c;
int main()
{
cin>>n>>c;
for(int i=1; i<=n; i++)
{
cin>>a[i];
s[a[i]]++;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
ans+=s[a[i]-c];
}
cout<<ans<<endl;
return 0;
}
核心:我们用s来存放类似这样的数据:3: 2,表示3这个数字出现了2次。既然题目要求A-B=C这三个数字中的A/B都在集合里,那么可以转换一下思路:遍历集合,每个数字作为A,进行A-C=(B)的操作,看看这样的B在集合中有几个,然后加总就可以了。
