Skip to content

喵哈~!主人,欢迎来到我的题解小站!今天我们要解决的是一道关于在玩具店里挑选玩具的可爱问题,B. Pair of Toys。别看题目里有大大的数字,其实它是一只很温顺的小猫咪哦,只要我们顺着它的毛摸,一下子就能解决啦!

题目大意

简单来说,就是这样的喵:

Tanechka 小姐姐的玩具店里有 n 个玩具,它们的价格分别是 1, 2, 3, ..., n。她想买两个不同的玩具,并且希望这两个玩具的总价正好是 k

我们需要计算,一共有多少种不同的购买组合呢?

这里有几个小小的注意事项哦:

  1. 玩具的价格是从 1 到 n 的连续整数。
  2. 必须选择两个不同的玩具,所以不能买两个价格一样的。
  3. 组合 (a, b)(b, a) 被认为是同一种,比如买了价格为 1 和 4 的玩具,和你先拿起 4 再拿起 1,是一回事啦~

解题思路

主人,我们来一起分析一下这个问题吧!这种问题最适合用一点点数学魔法来解决了,喵~

假设我们挑选的两个玩具价格分别是 ab。根据题目的要求,它们需要满足以下几个条件:

  1. 1 ≤ a ≤ n (玩具 a 的价格在可选范围内)
  2. 1 ≤ b ≤ n (玩具 b 的价格也在可选范围内)
  3. a + b = k (总价格是 k)
  4. a ≠ b (两个玩具不一样)
  5. (a, b)(b, a) 算一种。

为了处理第 5 个条件,我们可以加一个限制,比如说,我们总是让 a 的价格比 b 小,也就是 a < b。这样一来,每一种组合都只会被我们计算一次,而且 a ≠ b 这个条件也顺便满足了,是不是很巧妙呀?

现在,我们把所有条件整理一下,并且用 a 来表示一切。因为 a + b = k,所以 b = k - a。我们把这个代入到其他条件里去:

  • 原始条件 1: 1 ≤ a ≤ n
  • 原始条件 2: 1 ≤ b ≤ n => 1 ≤ k - a ≤ n
  • 我们加的条件: a < b => a < k - a

现在,我们把这些关于 a 的不等式一个个解开,看看 a 到底可以在哪个范围里取值:

  1. 1 ≤ k - a,我们可以得到 a ≤ k - 1
  2. k - a ≤ n,我们可以得到 a ≥ k - n
  3. a < k - a,我们可以得到 2a < k。因为 a 是整数,所以这等价于 a ≤ (k - 1) / 2

好啦,现在我们把所有关于 a 的限制条件都放在一起:

  • a ≥ 1
  • a ≥ k - n
  • a ≤ n
  • a ≤ (k - 1) / 2
  • a ≤ k - 1 (这个其实是多余的,因为 (k-1)/2 肯定比 k-1 小或者相等,所以满足 a ≤ (k-1)/2 就一定满足 a ≤ k-1 啦)

所以,a 的取值必须同时满足所有这些条件。这意味着 a 必须在一个确定的区间内。

  • a 的下界 (最小值)a 必须大于等于 1,也必须大于等于 k - n。所以,a 的下界就是这两者中较大的那一个,即 max(1, k - n)
  • a 的上界 (最大值)a 必须小于等于 n,也必须小于等于 (k - 1) / 2。所以,a 的上界就是这两者中较小的那一个,即 min(n, (k - 1) / 2)

这样,我们就得到了 a 的取值范围:[max(1, k - n), min(n, (k - 1) / 2)]

lower_bound = max(1LL, k - n)upper_bound = min(n, (k - 1) / 2)。 只要 lower_bound ≤ upper_bound,那么从 lower_boundupper_bound 之间的每一个整数都是一个合法的 a 值,对应一种独一无二的玩具组合。

那么,这个区间里有多少个整数呢?公式是 upper_bound - lower_bound + 1。 如果 lower_bound > upper_bound,说明不存在任何合法的 a,答案就是 0。

我们可以用 max(0LL, upper_bound - lower_bound + 1) 这个表达式来漂亮地处理这两种情况,喵~

题解代码

这是把我们的思路变成代码的样子,主人请看~

cpp
#include <iostream>
#include <algorithm>

int main() {
    // 使用这个可以让输入输出快一点点哦,对付大数据很有效~
    std::ios_base::sync_with_stdio(false);
    std::cin.tie(NULL);

    // n 和 k 可能会非常大,所以要用 long long 来装它们,不然会溢出的说!
    long long n, k;
    std::cin >> n >> k;

    // 我们来寻找满足条件的玩具组合 (a, b) 的数量。
    // 条件是:
    // 1. 1 <= a, b <= n
    // 2. a + b = k
    // 3. a != b
    // 为了不重复计数,我们强制 a < b。

    // 根据我们的推导,a 的下界是 1 和 k - n 中的较大值。
    // 这里的 1LL 是为了确保 max 函数在 long long 类型上运算。
    long long lower_bound_a = std::max(1LL, k - n);

    // a 的上界是 n 和 (k - 1) / 2 中的较小值。
    long long upper_bound_a = std::min(n, (k - 1) / 2);

    // 接下来计算在这个区间 [lower_bound_a, upper_bound_a] 中有多少个整数。
    long long count = 0;
    // 如果下界比上界还大,说明这个区间是空的,没有合法的 a。
    if (upper_bound_a >= lower_bound_a) {
        // 否则,数量就是 上界 - 下界 + 1。
        count = upper_bound_a - lower_bound_a + 1;
    }

    // 最后,把我们算出的答案打印出来~
    std::cout << count << std::endl;

    return 0;
}

知识点介绍

这道题虽然简单,但里面藏着一些很重要的知识点哦,我们一起来看看吧!

  1. 数学建模与不等式推导: 这是解决很多算法问题的核心能力!我们将文字描述的需求(买两个总价为k的不同玩具)转化为一系列精确的数学表达式和不等式 (a+b=k, a<b, 1<=a,b<=n)。然后通过代换和化简,最终推导出解的范围。这个过程就像侦探破案一样,从线索中找出真相,很有趣的喵!

  2. 处理大整数 (long long): 题目中明确指出 nk 的范围可以达到 10^14。C++ 中的 int 类型通常只能表示到 2 * 10^9 左右,是远远不够的。如果我们用 int,计算过程中就会发生溢出,导致结果错误。所以,只要看到数据范围超过 10^9,就要立刻想到使用 long long,这是一个非常重要的习惯!

  3. 区间内整数计数: 这是一个基础但非常有用的数学小技巧。要计算闭区间 [L, R] (L 和 R 都是整数) 中包含多少个整数,公式是 R - L + 1。比如 [3, 7] 中有 7 - 3 + 1 = 5 个整数 (3, 4, 5, 6, 7)。在我们的题解中,最后一步就是用这个公式来计算可行方案的数量。

  4. 代码的健壮性: 在计算数量时,我们先判断 upper_bound_a >= lower_bound_a。这可以防止在下界大于上界时,计算出 R - L + 1 为负数或零的情况。使用 max(0LL, R - L + 1) 是一种更简洁的写法,它能自动处理区间为空的情况,让代码更优雅、更不容易出错。

好啦,这次的题解就到这里啦!希望对主人有所帮助。如果还有不明白的地方,随时可以再来问我哦!喵~

Released under the MIT License.