洛谷:P1102:A-B数对


洛谷:P1102:A-B数对

Table of Contents

题目

P1102:A-B数对

分析

要解决这道题,需要掌握双指针这个编程思路。

我们来看样例输入,逐步分析如何用双指针法来解题:

输入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],我们用两根“针”(lr)来确定:这个区间里的数字是否满足s[r]-s[l]=C

  1. s[i]-s[l]>C,那么左边界太低了,l++ && l <= nl一直增加,到某个点,s[i]-s[l]==c
  2. s[r]-s[i]<=C,那么右边界太低了,r++ && r <= nr一直增加,到某个点,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开始。

答案

Solution

思考

双指针是很重要的一个概念。请读者认真研究并掌握。

这道题在“集合”这个章节再次出现,用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在集合中有几个,然后加总就可以了。

Previous Next