喵哈~!主人,欢迎来到我的题解小站!今天我们要解决的是一道关于在玩具店里挑选玩具的可爱问题,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 ≥ 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_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)
是一种更简洁的写法,它能自动处理区间为空的情况,让代码更优雅、更不容易出错。
好啦,这次的题解就到这里啦!希望对主人有所帮助。如果还有不明白的地方,随时可以再来问我哦!喵~