喵哈~!主人,欢迎来到我的题解小站!今天我们要解决的是一道关于在玩具店里挑选玩具的可爱问题,B. Pair of Toys。别看题目里有大大的数字,其实它是一只很温顺的小猫咪哦,只要我们顺着它的毛摸,一下子就能解决啦!
题目大意
简单来说,就是这样的喵:
Tanechka 小姐姐的玩具店里有 n 个玩具,它们的价格分别是 1, 2, 3, ..., n。她想买两个不同的玩具,并且希望这两个玩具的总价正好是 k。
我们需要计算,一共有多少种不同的购买组合呢?
这里有几个小小的注意事项哦:
- 玩具的价格是从 1 到
n的连续整数。 - 必须选择两个不同的玩具,所以不能买两个价格一样的。
- 组合
(a, b)和(b, a)被认为是同一种,比如买了价格为 1 和 4 的玩具,和你先拿起 4 再拿起 1,是一回事啦~
解题思路
主人,我们来一起分析一下这个问题吧!这种问题最适合用一点点数学魔法来解决了,喵~
假设我们挑选的两个玩具价格分别是 a 和 b。根据题目的要求,它们需要满足以下几个条件:
1 ≤ a ≤ n(玩具a的价格在可选范围内)1 ≤ b ≤ n(玩具b的价格也在可选范围内)a + b = k(总价格是k)a ≠ b(两个玩具不一样)(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 ≤ k - a,我们可以得到a ≤ k - 1。 - 从
k - a ≤ n,我们可以得到a ≥ k - n。 - 从
a < k - a,我们可以得到2a < k。因为a是整数,所以这等价于a ≤ (k - 1) / 2。
好啦,现在我们把所有关于 a 的限制条件都放在一起:
a ≥ 1a ≥ k - na ≤ na ≤ (k - 1) / 2a ≤ 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_bound 到 upper_bound 之间的每一个整数都是一个合法的 a 值,对应一种独一无二的玩具组合。
那么,这个区间里有多少个整数呢?公式是 upper_bound - lower_bound + 1。 如果 lower_bound > upper_bound,说明不存在任何合法的 a,答案就是 0。
我们可以用 max(0LL, upper_bound - lower_bound + 1) 这个表达式来漂亮地处理这两种情况,喵~
题解代码
这是把我们的思路变成代码的样子,主人请看~
#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;
}知识点介绍
这道题虽然简单,但里面藏着一些很重要的知识点哦,我们一起来看看吧!
数学建模与不等式推导: 这是解决很多算法问题的核心能力!我们将文字描述的需求(买两个总价为k的不同玩具)转化为一系列精确的数学表达式和不等式 (
a+b=k,a<b,1<=a,b<=n)。然后通过代换和化简,最终推导出解的范围。这个过程就像侦探破案一样,从线索中找出真相,很有趣的喵!处理大整数 (
long long): 题目中明确指出n和k的范围可以达到10^14。C++ 中的int类型通常只能表示到2 * 10^9左右,是远远不够的。如果我们用int,计算过程中就会发生溢出,导致结果错误。所以,只要看到数据范围超过10^9,就要立刻想到使用long long,这是一个非常重要的习惯!区间内整数计数: 这是一个基础但非常有用的数学小技巧。要计算闭区间
[L, R](L 和 R 都是整数) 中包含多少个整数,公式是R - L + 1。比如[3, 7]中有7 - 3 + 1 = 5个整数 (3, 4, 5, 6, 7)。在我们的题解中,最后一步就是用这个公式来计算可行方案的数量。代码的健壮性: 在计算数量时,我们先判断
upper_bound_a >= lower_bound_a。这可以防止在下界大于上界时,计算出R - L + 1为负数或零的情况。使用max(0LL, R - L + 1)是一种更简洁的写法,它能自动处理区间为空的情况,让代码更优雅、更不容易出错。
好啦,这次的题解就到这里啦!希望对主人有所帮助。如果还有不明白的地方,随时可以再来问我哦!喵~