Skip to content

喵~ 主人好呀!今天本喵要为主人细致地讲解一道来自 Codeforces 的有趣问题:1761A - Two Permutations。这道题看起来有点绕,但只要跟着本喵的思路,一步步分析,就会发现它其实是一只很乖的题目哦!

题目大意

这道题是这样哒:

给你三个整数 n, a, b。你需要判断,是否存在两个长度为 n 的排列 pq,同时满足下面两个条件:

  1. pq最长公共前缀 (Longest Common Prefix) 长度恰好为 a
  2. pq最长公共后缀 (Longest Common Suffix) 长度恰好为 b

如果存在这样的一对排列,就输出 "Yes",不然就输出 "No",喵~

小提示喵:

  • 排列:一个长度为 n 的排列,就是包含从 1 到 n 每个整数恰好一次的数组。比如 [1, 3, 2] 是一个长度为 3 的排列。
  • 最长公共前缀:两个数组从第一个元素开始,连续相等的元素序列的最大长度。
  • 最长公共后缀:两个数组从最后一个元素开始,连续相等的元素序列的最大长度。

题解方法

要解决这个问题,我们需要分两种情况来考虑,就像猫猫需要考虑是从左边还是右边跳上沙发一样,喵~ 这两种情况分别是:构造的两个排列 pq 完全相同,或者它们不完全相同

情况一:两个排列完全相同 (p = q)

如果 pq 是同一个排列,比如 p = q = [1, 2, ..., n],那么它们从头到尾每个位置上的元素都一样。

  • 它们的最长公共前缀就是整个排列,长度为 n
  • 它们的最长公共后缀也是整个排列,长度同样为 n

所以,只有当题目给定的 ab 同时等于 n 时,我们才可以通过构造两个完全相同的排列来满足条件。

结论一:如果 a == n 并且 b == n,答案是 "Yes"。

情况二:两个排列不完全相同 (p != q)

如果 pq 不一样,情况就变得有趣起来了,喵~

让我们想象一下这两个排列的结构:

p: [ (公共前缀) | (中间部分) | (公共后缀) ]
q: [ (公共前缀) | (中间部分) | (公共后缀) ]
  • 公共前缀:长度为 apq 在这部分完全相同。
  • 公共后缀:长度为 bpq 在这部分也完全相同。

为了保证最长公共前缀恰好a,那么在第 a+1 个位置上,pq 的元素必须不同(即 p[a+1] != q[a+1])。同理,为了保证最长公共后缀恰好b,在倒数第 b+1 个位置(即 n-b 位置)上,pq 的元素也必须不同

这意味着,pq 的差异一定出现在既不是公共前缀也不是公共后缀的“中间部分”。

我们来数一数这个中间部分有多少个位置。总共有 n 个位置,去掉前面 a 个和后面 b 个,中间部分的位置数量就是 n - a - b

  1. 如果 n - a - b < 0 (即 a + b > n): 这意味着前缀和后缀发生了重叠!比如 n=4, a=3, b=3,前缀是 [p1, p2, p3],后缀是 [p2, p3, p4]。这本身就很难构造,因为用于前缀和后缀的元素集合必须不冲突。更重要的是,我们总共只有 n 个数,却需要 a+b 个不重复的数来填充不重叠的前后缀,这是不可能的。所以直接 "No"。

  2. 如果 n - a - b = 0 (即 a + b = n): 这意味着前缀和后缀刚好接壤,没有中间部分。既然没有中间部分,pq 就没有地方可以变得不同了。因此 pq 必须完全相同。但这又回到了情况一,此时 ab 都必须是 n。如果 ab 小于 n,则无解。

  3. 如果 n - a - b = 1 (即 a + b = n - 1): 中间只有一个位置(第 a+1 个位置)和一个剩下的数字。pq 都只能把这个唯一的数字填在这个唯一的位置上。这就会导致 p[a+1] == q[a+1],从而使得公共前缀的长度变成了 a+1,不满足“恰好为 a”的要求。所以这种情况也是 "No"。

  4. 如果 n - a - b >= 2 (即 a + b <= n - 2): 太棒了!中间部分至少有两个位置和两个数字。比如中间有两个位置 ij,还有两个数字 xy。我们就可以在 p 中放 p[i]=x, p[j]=y,而在 q 中放 q[i]=y, q[j]=x。通过这样一个小小的交换,就保证了 pq 在中间部分是不同的,从而满足了最长公共前缀/后缀的长度限制!是不是很聪明呀?喵~

结论二:如果 a + b <= n - 2,我们总能构造出满足条件的两个不同排列,答案是 "Yes"。

总结

把两种情况合起来,最终的判断条件就是:

如果 (a == n && b == n) 或者 (a + b <= n - 2),那么就存在这样的一对排列。否则,就不存在。


题解

下面是实现这个逻辑的 C++ 代码,本喵加上了可爱的注释哦~

cpp
#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;
}

知识点介绍

这道题虽然简单,但也涉及了一些重要的概念呢,主人要记住喵!

  1. 排列 (Permutation) 排列是组合数学中的一个基本概念。简单来说,一个长度为 n 的排列就是把从 1 到 nn 个数字进行重排,每个数字必须使用一次且仅一次。就像把一排小鱼干重新排列,每条小鱼干都独一无二!

  2. 最长公共前缀/后缀 (LCP/LCS) 这是字符串和序列问题中非常常见的概念。LCP 是指两个序列从开头算起最长的相同部分,LCS 则是从末尾算起。理解这两个概念是解决本题的关键。

  3. 构造性证明 (Constructive Proof) 本题的解法就是一种构造性思维。我们不需要真的把 pq 这两个排列完整地构造出来,只需要证明在满足某些条件时,这样的排列可以被构造出来。而在不满足条件时,证明它们无法被构造。这是一种在算法竞赛中非常强大和常用的思维方式哦!

好啦,这次的题解就到这里啦~ 主人学会了吗?如果还有不懂的,可以随时再来问本喵哦!喵~

Released under the MIT License.