喵~ 主人好呀!今天本喵要为主人细致地讲解一道来自 Codeforces 的有趣问题:1761A - Two Permutations
。这道题看起来有点绕,但只要跟着本喵的思路,一步步分析,就会发现它其实是一只很乖的题目哦!
题目大意
这道题是这样哒:
给你三个整数 n
, a
, b
。你需要判断,是否存在两个长度为 n
的排列 p
和 q
,同时满足下面两个条件:
p
和q
的最长公共前缀 (Longest Common Prefix) 长度恰好为a
。p
和q
的最长公共后缀 (Longest Common Suffix) 长度恰好为b
。
如果存在这样的一对排列,就输出 "Yes",不然就输出 "No",喵~
小提示喵:
- 排列:一个长度为
n
的排列,就是包含从 1 到n
每个整数恰好一次的数组。比如[1, 3, 2]
是一个长度为 3 的排列。- 最长公共前缀:两个数组从第一个元素开始,连续相等的元素序列的最大长度。
- 最长公共后缀:两个数组从最后一个元素开始,连续相等的元素序列的最大长度。
题解方法
要解决这个问题,我们需要分两种情况来考虑,就像猫猫需要考虑是从左边还是右边跳上沙发一样,喵~ 这两种情况分别是:构造的两个排列 p
和 q
完全相同,或者它们不完全相同。
情况一:两个排列完全相同 (p = q)
如果 p
和 q
是同一个排列,比如 p = q = [1, 2, ..., n]
,那么它们从头到尾每个位置上的元素都一样。
- 它们的最长公共前缀就是整个排列,长度为
n
。 - 它们的最长公共后缀也是整个排列,长度同样为
n
。
所以,只有当题目给定的 a
和 b
同时等于 n
时,我们才可以通过构造两个完全相同的排列来满足条件。
结论一:如果
a == n
并且b == n
,答案是 "Yes"。
情况二:两个排列不完全相同 (p != q)
如果 p
和 q
不一样,情况就变得有趣起来了,喵~
让我们想象一下这两个排列的结构:
p: [ (公共前缀) | (中间部分) | (公共后缀) ]
q: [ (公共前缀) | (中间部分) | (公共后缀) ]
- 公共前缀:长度为
a
,p
和q
在这部分完全相同。 - 公共后缀:长度为
b
,p
和q
在这部分也完全相同。
为了保证最长公共前缀恰好是 a
,那么在第 a+1
个位置上,p
和 q
的元素必须不同(即 p[a+1] != q[a+1]
)。同理,为了保证最长公共后缀恰好是 b
,在倒数第 b+1
个位置(即 n-b
位置)上,p
和 q
的元素也必须不同。
这意味着,p
和 q
的差异一定出现在既不是公共前缀也不是公共后缀的“中间部分”。
我们来数一数这个中间部分有多少个位置。总共有 n
个位置,去掉前面 a
个和后面 b
个,中间部分的位置数量就是 n - a - b
。
如果
n - a - b < 0
(即a + b > n
): 这意味着前缀和后缀发生了重叠!比如n=4, a=3, b=3
,前缀是[p1, p2, p3]
,后缀是[p2, p3, p4]
。这本身就很难构造,因为用于前缀和后缀的元素集合必须不冲突。更重要的是,我们总共只有n
个数,却需要a+b
个不重复的数来填充不重叠的前后缀,这是不可能的。所以直接 "No"。如果
n - a - b = 0
(即a + b = n
): 这意味着前缀和后缀刚好接壤,没有中间部分。既然没有中间部分,p
和q
就没有地方可以变得不同了。因此p
和q
必须完全相同。但这又回到了情况一,此时a
和b
都必须是n
。如果a
或b
小于n
,则无解。如果
n - a - b = 1
(即a + b = n - 1
): 中间只有一个位置(第a+1
个位置)和一个剩下的数字。p
和q
都只能把这个唯一的数字填在这个唯一的位置上。这就会导致p[a+1] == q[a+1]
,从而使得公共前缀的长度变成了a+1
,不满足“恰好为a
”的要求。所以这种情况也是 "No"。如果
n - a - b >= 2
(即a + b <= n - 2
): 太棒了!中间部分至少有两个位置和两个数字。比如中间有两个位置i
和j
,还有两个数字x
和y
。我们就可以在p
中放p[i]=x, p[j]=y
,而在q
中放q[i]=y, q[j]=x
。通过这样一个小小的交换,就保证了p
和q
在中间部分是不同的,从而满足了最长公共前缀/后缀的长度限制!是不是很聪明呀?喵~
结论二:如果
a + b <= n - 2
,我们总能构造出满足条件的两个不同排列,答案是 "Yes"。
总结
把两种情况合起来,最终的判断条件就是:
如果 (a == n && b == n)
或者 (a + b <= n - 2)
,那么就存在这样的一对排列。否则,就不存在。
题解
下面是实现这个逻辑的 C++ 代码,本喵加上了可爱的注释哦~
#include <iostream>
void solve() {
int n, a, b;
std::cin >> n >> a >> b;
// 我们来判断是否存在这样的两个排列 p 和 q 喵~
//
// 情况一:p 和 q 完全相同 (p = q)
// 这种情况下,最长公共前缀和后缀的长度都是 n。
// 所以,只有当 a 和 b 都等于 n 时,这种情况才成立。
// 我们可以构造 p = q = [1, 2, ..., n]。
bool case1 = (a == n && b == n);
// 情况二:p 和 q 不相同 (p != q)
// 公共前缀占了 a 个位置,公共后缀占了 b 个位置。
// 为了让前缀长度 *恰好* 是 a,p[a+1] 必须不等于 q[a+1]。
// 这就要求 p 和 q 必须在非前缀和非后缀的“中间部分”有所不同。
//
// 中间部分的长度是 n - a - b。
// 如果 n - a - b < 2 (即 0 或 1),我们就没有足够空间来制造不同。
// - 如果是 0,前缀后缀相连,p和q只能相同,矛盾。
// - 如果是 1,中间只有一个位置和一个数,p和q也只能相同,矛盾。
// 如果 n - a - b >= 2,我们就有至少两个位置和两个数,
// 可以通过交换它们来构造出不同的中间部分,从而满足条件。
// 这个条件可以写成 a + b <= n - 2。
bool case2 = (a + b <= n - 2);
if (case1 || case2) {
std::cout << "Yes\n";
} else {
std::cout << "No\n";
}
}
int main() {
// 加速输入输出,让程序跑得像猫一样快!
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
int t;
std::cin >> t;
while (t--) {
solve();
}
return 0;
}
知识点介绍
这道题虽然简单,但也涉及了一些重要的概念呢,主人要记住喵!
排列 (Permutation) 排列是组合数学中的一个基本概念。简单来说,一个长度为
n
的排列就是把从 1 到n
的n
个数字进行重排,每个数字必须使用一次且仅一次。就像把一排小鱼干重新排列,每条小鱼干都独一无二!最长公共前缀/后缀 (LCP/LCS) 这是字符串和序列问题中非常常见的概念。LCP 是指两个序列从开头算起最长的相同部分,LCS 则是从末尾算起。理解这两个概念是解决本题的关键。
构造性证明 (Constructive Proof) 本题的解法就是一种构造性思维。我们不需要真的把
p
和q
这两个排列完整地构造出来,只需要证明在满足某些条件时,这样的排列可以被构造出来。而在不满足条件时,证明它们无法被构造。这是一种在算法竞赛中非常强大和常用的思维方式哦!
好啦,这次的题解就到这里啦~ 主人学会了吗?如果还有不懂的,可以随时再来问本喵哦!喵~