[ICPC2020上海E] The Journey of Geor Autumn - 组合数学

Description

对于给定的 \(n,k \le 10^7\),求有多少个满足这样条件的 \(1..n\) 全排列:对于任意 \(i >k\)\(a_i > \min_{j \in [i-k,i)} a_j\).

Solution

最小的数必须放在 \([1,k]\) 中的任意位置 \(pos\) 上。一旦这个数确定,那么 \([1,pos)\) 这段位置区间中可以任意填数,\((pos,n]\) 这一段构成一个递归的子问题。

\(f[n]\) 表示对于一个长度为 \(n\) 的序列的答案,则

\[f[n] = \sum_{j=1}^{\min(i,k)} \binom{i-1}{j-1} f(i-j) (j-1)! \]

变形得到

\[\frac {f(i)} {i!} = \frac 1 i \sum_{j=1}^{\min(i,k)} \frac{f(i-j)}{(i-j)!} \]

前缀和优化一下即可。

阶乘辅助是线性预处理逆元的简单而有效的手段。

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

#define int long long
const int mod = 998244353;
const int N = 1e+7 + 5;

int s[N], inv[N], ifac[N], fac[N], n, k;

int qpow(int p, int q)
{
    return (q & 1 ? p : 1) * (q ? qpow(p * p % mod, q / 2) : 1) % mod;
}

signed main()
{
    ios::sync_with_stdio(false);
    cin >> n >> k;
    fac[0] = 1;
    for (int i = 1; i <= n + 1; i++)
        fac[i] = fac[i - 1] * i % mod;
    ifac[n + 1] = qpow(fac[n + 1], mod - 2);
    for (int i = n; i >= 0; i--)
        ifac[i] = ifac[i + 1] * (i + 1) % mod;
    for (int i = 1; i <= n; i++)
        inv[i] = ifac[i] * fac[i - 1] % mod;
    s[0] = 1;
    for (int i = 1; i <= n; i++)
        s[i] = (s[i - 1] + (s[i - 1] - (i - k - 1 < 0 ? 0 : s[i - k - 1]) + mod) % mod * inv[i] % mod) % mod;
    cout << (s[n] - s[n - 1] + mod) % mod * fac[n] % mod << endl;
}

相关文章: