[luogu p1102] A-B 数对

传送门

A-B 数对

题目描述

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 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\%\) 的数据,\(1 \leq N \leq 2000\)

对于 \(100\%\) 的数据,\(1 \leq N \leq 2 \times 10^5\)

保证所有输入数据都在 \(32\) 位带符号整数范围内。

2017/4/29 新添数据两组

分析

这道题是我在二分题单里遇到的,但是这道题的思路我死活想不到二分……

我的思路是这样的,首先把数排序一遍,然后如果A和C都确定了,那么B也只能是那个数。题目中说

不同位置的数字一样的数对算不同的数对

所以每一次扫到一个A,后面每一个B都是一种方案,也就是说方案数多了 B的个数 种。

思路也就很明显了,

  • 排序一遍数字
  • 从前往后扫,把这个数当成A
  • 通过A和C算出B,查找序列中B的个数
  • 方案数加上B的个数

思考到这里我终于明白为什么这道题在二分题单里了。我们来看最后一条:

方案数加上B的个数

这个B的个数怎么求呢?自然有一种方案是从头到尾扫一遍,不过这样的复杂度是\(\operatorname{O}(N)\),外层循环也是\(\operatorname{O}(N)\),整体的复杂度就是\(\operatorname{O}(N^2)\)了。然而数据范围是\(1 \le N \le 2 \times 10^5\)\(\operatorname{O}(N^2)\)的复杂度显然不行。

那怎么办呢?

这个时候二分就要出场了。

不要忘记了数是排序的,也就是说数字为B的数在这个序列中是连续的。那么我们只要算出这个序列的头和尾就能算出数字为B的个数。

算出一个连续为B序列的头和尾,这就是裸到不能再裸的二分了。

那么是手写二分还是STL呢?这里就根据你的习惯来了。这里我选择STL,也就是lower_bound和upper_bound。

思路理明白,代码就简单了。

代码

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2020-06-13 10:29:10 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2020-06-13 10:53:14
 */
#include <iostream>
#include <cstdio>
#include <algorithm>

inline long read() {
    long x;
    std :: cin >> x;
    return x;
}

const int maxn = 200005;

long a[maxn];

int main() {
    long n = read(), c = read(), ans = 0;
    for (int i = 1; i <= n; ++i) a[i] = read();
    std :: sort(a + 1, a + n + 1);
    for (int i = 1; i <= n; ++i) 
        ans += (std :: upper_bound(a + 1, a + n + 1, a[i] + c) - a) - (std :: lower_bound(a + 1, a + n + 1, a[i] + c) - a);
    std :: cout << ans << std :: endl;
    return 0;
}

评测记录

评测记录

posted @ 2020-06-13 10:55  东北小蟹蟹  阅读(139)  评论(0编辑  收藏  举报