题目链接牛客Link

思路

  • 偶数 × 任意数 = 偶数
  • 奇数 × 奇数 = 奇数

所以题目排列中不能出现两个相邻的奇数

~ 中:

  • 奇数个数

  • 偶数个数

插空法计数

相邻两个数乘积为偶数 ⇔ 不能有两个奇数相邻

  1. 先排列偶数
    • 偶数共有 个,任意顺序合法,方案数:
  2. 插入奇数
    • 偶数排好后产生 个空位,为避免奇数相邻,每个空位最多放 个奇数,从中选 个空位:
  3. 排列奇数
    • 奇数共有 个,任意排列,方案数:

所以:

知识拓展:逆元

逆元的概念

在模 下, 的逆元,当且仅当:

逆元存在的条件是:

例子

费马小定理(模质数)

  1. 定理内容

是质数,且 ,则:

等价地:

逆元公式

例子

计算:

扩展欧几里得法(一般模数)

,使得:

如果 ,则 即为逆元:

  1. 费马小定理法(模质数 ):
// a^b mod p
long long mod_pow(long long a, long long b, long long p) {
    long long res = 1;
    a %= p;
    while (b > 0) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
        b >>= 1;
    }
    return res;
}

// a^-1 mod p
long long mod_inverse(long long a, long long p) {
    return mod_pow(a, p - 2, p); // 对应公式 a^(p-2) ≡ a^-1 (mod p)
}
  1. 扩展欧几里得法(一般模数 ):
long long exgcd(long long a, long long b, long long &x, long long &y) {
    if (b == 0) { x = 1; y = 0; return a; }
    long long d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

// a^-1 mod m
long long mod_inverse_general(long long a, long long m) {
    long long x, y;
    long long g = exgcd(a, m, x, y);
    if (g != 1) return -1; // 无逆元
    return (x % m + m) % m; // 确保正数
}

代码实现

C++

#include <bits/stdc++.h>
using namespace std;

const long long MOD = 1e9 + 7;

long long mod_pow(long long a, long long b, long long mod) {
    long long res = 1;
    a %= mod;
    while (b > 0) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}

int main() {
    int n;
    cin >> n;

    int odd = (n + 1) / 2;
    int even = n / 2;

    // 预处理阶乘和逆阶乘
    vector<long long> fact(n + 2, 1), invfact(n + 2, 1);

    for (int i = 1; i <= n + 1; ++i) {
        fact[i] = fact[i - 1] * i % MOD;
    }

    // 计算逆阶乘
    invfact[n + 1] = mod_pow(fact[n + 1], MOD - 2, MOD);
    for (int i = n + 1; i > 0; --i) {
        invfact[i - 1] = invfact[i] * i % MOD;
    }

    // 组合数 C(a, b)
    auto C = [&](int a, int b) -> long long {
        if (b < 0 || b > a) return 0;
        return fact[a] * invfact[b] % MOD * invfact[a - b] % MOD;
    };

    // 计算答案
    long long ans = fact[even] * C(even + 1, odd) % MOD;
    ans = ans * fact[odd] % MOD;

    cout << ans << endl;

    return 0;
}

Python

MOD = 10**9 + 7

n = int(input().strip())

# 计算奇偶数个数
odd = (n + 1) // 2
even = n // 2

# 预处理阶乘和逆元
fact = [1] * (n + 2)
invfact = [1] * (n + 2)

for i in range(1, n + 2):
    fact[i] = fact[i - 1] * i % MOD

# 计算逆阶乘
invfact[n + 1] = pow(fact[n + 1], MOD - 2, MOD)
for i in range(n + 1, 0, -1):
    invfact[i - 1] = invfact[i] * i % MOD

# 组合数 C(a, b)
def C(a, b):
    if b < 0 or b > a:
        return 0
    return fact[a] * invfact[b] % MOD * invfact[a - b] % MOD

# 总数 = e! * C(e+1, o) * o!
ans = fact[even] * C(even + 1, odd) % MOD
ans = ans * fact[odd] % MOD

print(ans)

优化

如果你能想到化简公式和本题中恒有 ,即

原公式:

尝试化简

组合数定义为:

代入原公式:

可以与分母抵消:

本题中:

所以:

  • 偶数时,
  • 奇数时,

代码实现

C++

#include <bits/stdc++.h>
using namespace std;

const long long MOD = 1e9 + 7;

int main() {
    int n;
    cin >> n;

    int e = n / 2;

    // 预处理阶乘
    vector<long long> fact(n + 2, 1);
    for (int i = 1; i <= n + 1; ++i) {
        fact[i] = fact[i - 1] * i % MOD;
    }

    // 计算答案
    long long ans = fact[e] * fact[e + 1] % MOD;

    cout << ans << endl;
    return 0;
}

Python

MOD = 10**9 + 7

n = int(input().strip())

# 计算偶数个数 e
e = n // 2

# 计算阶乘
fact = [1] * (n + 2)
for i in range(1, n + 2):
    fact[i] = fact[i-1] * i % MOD

# 答案 = e! * (e+1)! % MOD
ans = fact[e] * fact[e+1] % MOD

print(ans)