第一类斯科特数

第一类斯科特数 表示 个不同元素排列成恰好个”循环”的排列数

对于循环,我们可以理解为均为一类循环,因为将这些数无限写下去的形式上是一致的

例子
  • ,即所有排列都在一个大循环中,此时有两种方案{(1,2,3),(1,3,2)}
  • ,此时有 (1)(2,3) | (2)(1,3) | (3)(1,2) 这样的组合方式
  • ,此时每个元素各自组成一个循环

第一类斯科特数递推公式

其中递推的初始条件我们规定为

上面为无符号第一类斯科特数的递归方式 如果想获得有符号类的可以使用 方式得到

应用:

  • 在排列的循环结构或者有向图的环的计数中使用
  • 阶乘多项式展开中使用
  • 其中 即有符号的第一类斯科特数

第二类斯科特数

第二类斯科特数 表示将 个不同元素划分为恰好 个非空无序集合的方案数

对与三个不同元素{1,2,3}划分

  • ,即所有元素处于一个集合中
  • :

对第二类斯特林数有递推公式

  • 初始条件我们规定

对与第二类斯特林数,我们有显式公式:

可以在复杂度下计算出其值,可以结合快速组合数快速幂计算,在最后给出板子

第一类欧拉数

i64 ksm(i64 a, i64 b, i64 mod = MOD)
{
    i64 res = 1;
    while (b) {
        if (b & 1) res = res * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return res;
}
 
struct Stirling
{
    vector<i64> fac, inv_fac;
    i64 MAX_N;
    Stirling(i64 MAXK)
    {
        MAX_N = MAXK;
        fac.resize(MAX_N);
        inv_fac.resize(MAX_N);
        init();
    }
    // 预处理 [0, MOD-1] 阶乘及逆元
    void init()
    {
        fac[0] = 1;
        for (i64 i = 1; i < MAX_N; i++) fac[i] = fac[i - 1] * i % MOD;
        inv_fac[MAX_N - 1] = ksm(fac[MAX_N - 1], MOD - 2);
        for (i64 i = MAX_N - 2; i >= 0; i--) inv_fac[i] = inv_fac[i + 1] * (i + 1) % MOD;
    }
    // 计算 C(n, k) mod MOD,要求 n, k < MOD
    inline i64 C_small(i64 n, i64 k)
    {
        if (k < 0 || k > n) return 0;
        return fac[n] * inv_fac[k] % MOD * inv_fac[n - k] % MOD;
    }
    // Lucas 定理递归求解大组合数
    i64 C(i64 n, i64 k)
    {
        if (k < 0 || k > n) return 0;
        if (n < MOD && k < MOD) return C_small(n, k);
        return C(n / MOD, k / MOD) * C_small(n % MOD, k % MOD) % MOD;
    }
    i64 A(int n, int k) const
    {
        if (k < 0 || k > n) return 0;
        return fac[n] * inv_fac[n - k];
    }
    // 第二类斯特林数的显式表示
    i64 get_Stirling(int n, int k)
    {
        if (k < 0 || k > n) return 0;
        i64 res = 0;
        for (int i = 0; i <= k; ++i) {
            i64 term = C(k, i) * ksm(k - i, n) % MOD;
            // 符号: (-1)^i,根据公式 S(n,k) = 1/k! * sum_{i=0..k} (-1)^i C(k,i) (k-i)^n
            if (i & 1)
                res = (res - term + MOD) % MOD;
            else
                res = (res + term) % MOD;
        }
        res = res * inv_fac[k] % MOD; // 除以 k!
        return res;
    }
    // 第二类欧拉数计算
    i64 sec_Eulr(int n, int k)
    {
        return (get_Stirling(n, k) * fac[k]) % MOD;
    }
};

其实就是在快速组合数上进行了添加