题解:P14352 排序

· · 题解

题目大意

给定一个长度为 n 的排列 a,定义排序算法 A(k) 如下:

问有多少个排列在执行 A(k) 后会变成完全升序的,答案对 998244353 取模。

思路

冒泡排序性质

冒泡排序有一个重要性质:

因此,执行 k 轮冒泡排序后,最大的 k 个元素(即 n, n-1, \dots, n-k+1)已经在它们的正确位置上。

转化

一个排列可以唯一地由其逆序表 (e_1, e_2, \dots, e_n) 表示,其中 e_i 表示在 a_i 右边且比 a_i 小的元素个数。

冒泡排序每轮每个元素最多左移一次,因此:

所以,排列在 k 轮冒泡排序后完全有序的充要条件是:对于所有 ie_i \leq k

公式推导

由于 e_i 的取值范围是 0n-i,加上限制 e_i \leq k 后,e_i 的取值范围是 0\min(k, n-i)

m = \min(k, n-1),我们可以分两种情况:

  1. k \geq n

    • 所有排列都能在 n-1 轮内排好序。
    • 答案就是 n!
  2. k < n

    • 对于 i \leq n-kn-i \geq k,所以 e_ik+1 种取值。
    • 对于 i > n-kn-i < k,所以 e_in-i+1 种取值。
    • 总方案数为: \text{ans} = (k+1)^{n-k} \times \prod_{j=1}^k (j+1) = (k+1)^{n-k} \times k!

最终公式

\text{ans} = \begin{cases} n! & \text{if } k \geq n \\ (k+1)^{n-k} \times k! \bmod 998244353 & \text{if } k < n \end{cases}

算法实现

复杂度分析

代码


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

const int MOD = 998244353;

// 计算 a^b % MOD
long long qpow(long long a, long long b) {
    long long res = 1;
    while (b) {
        if (b & 1) res = res * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return res;
}

int main() {
    long long n, k;
    cin >> n >> k;
    if (k >= n) {
        // k >= n 时,所有排列都能排好序。
        long long ans = 1;
        for (int i = 1; i <= n; i++) {
            ans = ans * i % MOD;
        }
        cout << ans << endl;
    } else {
        // k < n 时,答案为 (k+1)^(n-k) * k!。
        long long fact = 1;
        for (int i = 1; i <= k; i++) {
            fact = fact * i % MOD;
        }
        long long ans = fact * qpow(k + 1, n - k) % MOD;
        cout << ans << endl;
    }

    return 0; // 好习惯。
}