刷题笔记-好排列的数量计算 | 豆包MarsCode AI刷题

268 阅读3分钟

最近在刷题时遇到了一个有趣的排列问题,称为“好排列”。题目要求计算长度为 nn 的好排列的数量,其中好排列的定义是相邻的两个数的乘积均为偶数。由于结果可能很大,需要对 109+710^9 + 7 取模。

解题思路

首先,我们需要理解什么情况下相邻两个数的乘积为偶数。只有当至少有一个数为偶数时,乘积才会是偶数。因此,要使所有相邻的数的乘积均为偶数,我们需要确保不存在两个奇数相邻的情况。

换句话说,我们需要将所有的奇数安排在排列中,使得它们之间至少隔一个偶数。基于这个思路,我们可以将问题转化为:

  1. 计算有多少种方法将奇数和偶数安排,使得没有两个奇数相邻。
  2. 分别排列奇数和偶数的顺序。

算法设计

1. 统计奇数和偶数的数量

  • 计算奇数的数量 no=n2n_o = \left\lceil \dfrac{n}{2} \right\rceil
  • 计算偶数的数量 ne=n2n_e = \left\lfloor \dfrac{n}{2} \right\rfloor

2. 判断可行性

如果奇数的数量超过了偶数数量加一,即 no>ne+1n_o > n_e + 1,那么无论如何排列,都无法避免两个奇数相邻的情况,此时答案为 0。

3. 计算排列方式

(a) 安排奇数的位置

我们有 ne+1n_e + 1 个空位可供奇数放置(在偶数之间以及两端)。我们需要从中选择 non_o 个位置放置奇数。方法数为组合数 C(ne+1,no)C(n_e + 1, n_o)

(b) 排列奇数和偶数的顺序

  • 奇数可以有 no!n_o! 种排列方式。
  • 偶数可以有 ne!n_e! 种排列方式。

(c) 总的排列数

总的排列方式数为:

总方法数=C(ne+1,no)×no!×ne!\text{总方法数} = C(n_e + 1, n_o) \times n_o! \times n_e!

4. 模运算和逆元计算

由于需要对 109+710^9 + 7 取模,并且涉及到组合数和阶乘的计算,我们需要预处理阶乘和逆元,以高效计算组合数。

  • 预处理阶乘 fact[i]=i!mod(109+7)\text{fact}[i] = i! \mod (10^9 + 7)
  • 计算阶乘的逆元 inv_fact[i]\text{inv\_fact}[i],利用费马小定理 ap2modpa^{p-2} \mod p

代码实现

def solution(n: int) -> int:
    mod = 10 ** 9 + 7
    n_o = (n + 1) // 2  # 奇数的数量
    n_e = n // 2        # 偶数的数量
    if n_o > n_e + 1:
        return 0

    max_n = n_e + n_o
    fact = [1] * (max_n + 1)
    inv_fact = [1] * (max_n + 1)
    for i in range(1, max_n + 1):
        fact[i] = fact[i - 1] * i % mod
    inv_fact[max_n] = pow(fact[max_n], mod - 2, mod)
    for i in range(max_n - 1, -1, -1):
        inv_fact[i] = inv_fact[i + 1] * (i + 1) % mod

    def comb(n, k):
        if k < 0 or k > n:
            return 0
        return fact[n] * inv_fact[k] % mod * inv_fact[n - k] % mod

    ways = comb(n_e + 1, n_o)
    ways = ways * fact[n_o] % mod
    ways = ways * fact[n_e] % mod
    return ways % mod

感悟

这道题考查了对排列组合的理解以及对模运算的掌握。在解决过程中,我体会到以下几点:

  1. 转化问题的能力:原始问题看似复杂,但通过分析,我们将其转化为避免奇数相邻的问题,大大简化了思考难度。

  2. 数学模型的建立:利用组合数学来计算位置安排和元素排列,使得问题有了明确的计算方法。

  3. 算法的优化:由于涉及大量的阶乘和组合数计算,直接计算会导致时间和空间复杂度过高。通过预处理阶乘和逆元,利用模运算性质,优化了计算效率。

  4. 细心和耐心:在处理边界条件和特殊情况(如奇数数量过多)时,需要仔细分析,避免出错。