组合数学

组合数学


例1:P3197 [HNOI2008] 越狱

题目简意

有 \(n\) 个房间,每个房间的犯人可以选择 \(m\) 种宗教之一。如果相邻两个房间的犯人信仰同一宗教,则发生越狱。求发生越狱的方案数,对 \(100003\) 取模。

思路分析

直接求发生越狱的方案数较困难,采用正难则反策略:

  • 总方案数:每个房间有 \(m\) 种选择,共 \(m^n\) 种。
  • 不发生越狱的方案数:第一个房间有 \(m\) 种选择;之后每个房间只要与前一个不同即可,各有 \(m-1\) 种选择,共 \(m \times (m-1)^{n-1}\)。
  • 答案 = 总方案数 − 不发生越狱方案数 = \(m^n - m \times (m-1)^{n-1}\)。

注意取模处理,防止结果为负数。

时空复杂度

  • 时间复杂度:\(O(\log n)\)(快速幂)
  • 空间复杂度:\(O(1)\)

代码

#include <cstdio>
using namespace std;

const int MOD = 100003;

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

int main() {
    long long m, n;
    scanf("%lld%lld", &m, &n);
    
    long long total = qpow(m, n);                   // 总方案数 m^n
    long long noEscape = m * qpow(m - 1, n - 1) % MOD; // 不发生越狱 m*(m-1)^(n-1)
    
    long long ans = (total - noEscape + MOD) % MOD;
    printf("%lld\n", ans);
    
    return 0;
}

例2:P5664 [CSP-S 2019] Emiya 家今天的饭

题目简意

给定一个 \(n \times m\) 的矩阵,\(a_{i,j}\) 表示用第 \(i\) 种烹饪方法和第 \(j\) 种主要食材可以做出 \(a_{i,j}\) 道不同的菜。要求:

  1. 选的菜非空;
  2. 每道菜的烹饪方法互不相同(即每行最多选一个);
  3. 每种主要食材的菜数不超过选菜总数的一半。

求满足条件的方案总数。

思路分析

采用正难则反:答案 = 所有方案数 − 不合法方案数。

所有方案数:对每行,可以选择不做菜,或从该行 \(m\) 种食材中选一种做一道菜。设 \(sum_i = \sum_{j=1}^{m} a_{i,j}\),则前 \(i\) 行的总方案数为 \(\prod_{i=1}^{n} (sum_i + 1)\)。减去全不选的 1 种空集情况。

不合法方案数:不合法方案中,有且仅有一种食材的出现次数超过总数的一半。枚举这个“超限”食材 \(c\),用 DP 计算:

设 \(dp[d]\) 表示当前考虑前 \(i\) 行,超限食材出现次数 − 其他食材出现次数的差值为 \(d\) 的方案数。

  • 不选:差值不变
  • 选超限食材 \(c\):差值 \(+1\),方案数乘 \(a_{i,c}\)
  • 选其他食材:差值 \(-1\),方案数乘 \((sum_i - a_{i,c})\)

最终差值 \(> 0\) 的方案数即为以 \(c\) 为超限食材的不合法方案数。

将 \(m\) 种食材的不合法方案数求和,用总方案数减去即可。

时空复杂度

  • 时间复杂度:\(O(n^2 m)\)
  • 空间复杂度:\(O(n)\)(滚动数组优化后)

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int MAXN = 105;
const int MAXM = 2005;
const int MOD = 998244353;

int n, m;
long long a[MAXN][MAXM];
long long sum[MAXN];
long long dp[MAXN * 2]; // 偏移量为 n,差值范围 [-n, n]

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            scanf("%lld", &a[i][j]);
            sum[i] = (sum[i] + a[i][j]) % MOD;
        }
    }
    
    // 1. 计算所有方案数(包含空集)
    long long all = 1;
    for (int i = 1; i <= n; i++) {
        all = all * (sum[i] + 1) % MOD;
    }
    all = (all - 1 + MOD) % MOD; // 去掉空集
    
    // 2. 计算不合法方案数
    long long bad = 0;
    for (int c = 1; c <= m; c++) { // 枚举超限食材
        memset(dp, 0, sizeof(dp));
        dp[n] = 1; // 偏移 n,表示差值为 0
        
        for (int i = 1; i <= n; i++) {
            long long other = (sum[i] - a[i][c] + MOD) % MOD;
            long long ndp[MAXN * 2] = {0};
            
            for (int d = 0; d <= 2 * n; d++) {
                if (dp[d] == 0) continue;
                // 不选
                ndp[d] = (ndp[d] + dp[d]) % MOD;
                // 选超限食材 c
                if (d + 1 <= 2 * n) {
                    ndp[d + 1] = (ndp[d + 1] + dp[d] * a[i][c]) % MOD;
                }
                // 选其他食材
                if (d - 1 >= 0) {
                    ndp[d - 1] = (ndp[d - 1] + dp[d] * other) % MOD;
                }
            }
            memcpy(dp, ndp, sizeof(dp));
        }
        
        // 差值 > 0 的方案数(即超限食材出现次数 > 其他食材出现次数)
        for (int d = n + 1; d <= 2 * n; d++) {
            bad = (bad + dp[d]) % MOD;
        }
    }
    
    long long ans = (all - bad + MOD) % MOD;
    printf("%lld\n", ans);
    
    return 0;
}

例3:拿糖果

无对应题目(用户提供的题号列表中没有该题)。


例4 / 例11:P1287 盒子与球

题目简意

有 \(r\) 个互不相同的盒子和 \(n\) 个互不相同的球,要将 \(n\) 个球全部放入 \(r\) 个盒子中,不允许有空盒子,求方案数。

思路分析

方法一:容斥原理

  • 不考虑空盒限制时,每个球有 \(r\) 种选择,总方案数为 \(r^n\)
  • 减去有空盒的情况,用容斥:

    \[\text{ans} = \sum_{k=0}^{r} (-1)^k \binom{r}{k} (r-k)^n \]

    其中 \(k\) 表示强制空盒的数量。

方法二:第二类斯特林数 + 排列

  • 先将 \(n\) 个不同的球分成 \(r\) 个非空无标号集合,方案数为第二类斯特林数 \(S(n,r)\)
  • 再将 \(r\) 个集合分配给 \(r\) 个不同的盒子,乘 \(r!\)
  • 答案为 \(S(n,r) \times r!\)

递推式:\(S(i,j) = S(i-1,j-1) + j \times S(i-1,j)\)

时空复杂度

  • 时间复杂度:\(O(nr)\)(DP 递推)
  • 空间复杂度:\(O(nr)\) 或 \(O(r)\)(滚动数组)

代码(第二类斯特林数 + 排列)

#include <cstdio>
using namespace std;

long long s[15][15]; // s[i][j] = 第二类斯特林数

int main() {
    int n, r;
    scanf("%d%d", &n, &r);
    
    // 第二类斯特林数递推
    s[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= r; j++) {
            s[i][j] = s[i - 1][j - 1] + j * s[i - 1][j];
        }
    }
    
    // 乘以 r!(盒子互不相同)
    long long fact = 1;
    for (int i = 1; i <= r; i++) fact *= i;
    
    printf("%lld\n", s[n][r] * fact);
    
    return 0;
}

例5:P1450 [HAOI2008] 硬币购物

题目简意

有 4 种面值的硬币,面值分别为 \(c_1, c_2, c_3, c_4\)。给出 \(T\) 组询问,每组给定每种硬币的数量限制 \(d_i\) 和需要凑出的总金额 \(s\),求用不超过数量限制的硬币凑出 \(s\) 的方案数。

思路分析

直接多重背包每组询问都会超时。

容斥原理:

  1. 先用完全背包预处理出无数量限制时凑出任意金额 \(s\) 的方案数 \(dp[s]\)。
  2. 对于一组询问,答案 = 无限制方案数 − 某些硬币超限的方案数。
  3. 若第 \(i\) 种硬币超限,则至少用了 \(d_i+1\) 枚,先强制扣除 \((d_i+1) \times c_i\),剩余金额任意凑。
  4. 用容斥枚举 4 种硬币超限/不超限的所有 \(2^4\) 种组合:奇数个超限则减,偶数个超限则加。

时空复杂度

  • 预处理:\(O(4 \times s_{max})\)
  • 每组询问:\(O(2^4) = O(16)\)
  • 空间复杂度:\(O(s_{max})\)

代码

#include <cstdio>
#include <cstring>
using namespace std;

const int MAXS = 100000;
long long dp[MAXS + 5];
int c[5], d[5];

int main() {
    for (int i = 1; i <= 4; i++) scanf("%d", &c[i]);
    
    // 完全背包预处理:无限制方案数
    dp[0] = 1;
    for (int i = 1; i <= 4; i++) {
        for (int j = c[i]; j <= MAXS; j++) {
            dp[j] += dp[j - c[i]];
        }
    }
    
    int T;
    scanf("%d", &T);
    while (T--) {
        for (int i = 1; i <= 4; i++) scanf("%d", &d[i]);
        int s;
        scanf("%d", &s);
        
        long long ans = 0;
        // 枚举 16 种超限组合(二进制位为 1 表示该硬币超限)
        for (int mask = 0; mask < 16; mask++) {
            long long remain = s;
            int cnt = 0; // 超限硬币个数
            for (int i = 1; i <= 4; i++) {
                if (mask & (1 << (i - 1))) {
                    remain -= 1LL * c[i] * (d[i] + 1);
                    cnt++;
                }
            }
            if (remain < 0) continue;
            // 容斥:奇数个超限减,偶数个超限加
            if (cnt & 1) ans -= dp[remain];
            else ans += dp[remain];
        }
        
        printf("%lld\n", ans);
    }
    
    return 0;
}

例6:P5520 [yLOI2019] 青原樱

题目简意

有 \(n\) 个花盆排成一行,要种 \(m\) 朵互不相同的花,每朵花不能种在相邻的花盆中,求方案数对 \(mod\) 取模。

思路分析

隔板法(插空法) :

  • 先不考虑花的互异性,从 \(n\) 个位置中选出 \(m\) 个互不相邻的位置
  • 在 \(n-m\) 个空位(未种花的位置)形成的 \(n-m+1\) 个空隙中,选 \(m\) 个插入花
  • 组合数为 \(\binom{n-m+1}{m}\)
  • 再乘以 \(m!\)(\(m\) 朵花互不相同)
  • 化简:\(\binom{n-m+1}{m} \times m! = \frac{(n-m+1)!}{(n-2m+1)!} = \prod_{i=n-2m+2}^{n-m+1} i\)

时空复杂度

  • 时间复杂度:\(O(m)\)(循环累乘)
  • 空间复杂度:\(O(1)\)

代码

#include <cstdio>
using namespace std;

int main() {
    long long n, m, mod;
    // 注意输入格式:第一个数是无用的类型标识,跳过
    scanf("%*d%lld%lld%lld", &n, &m, &mod);
    
    long long ans = 1;
    // 计算 ∏_{i=n-2m+2}^{n-m+1} i
    for (long long i = n - 2 * m + 2; i <= n - m + 1; i++) {
        ans = ans * i % mod;
    }
    
    printf("%lld\n", ans);
    return 0;
}

例7:P4071 [SDOI2016] 排列计数

题目简意

求有多少个 \(1\) 到 \(n\) 的排列,恰好有 \(m\) 个位置满足 \(a_i = i\)(即恰好 \(m\) 个“稳定”位置),答案对 \(10^9+7\) 取模。

思路分析

  • 先从 \(n\) 个位置中选出 \(m\) 个稳定位置:\(\binom{n}{m}\)
  • 剩下的 \(n-m\) 个位置必须全部错排(没有一个元素在原来的位置)
  • 错排数 \(D_k\) 的递推公式:\(D_0 = 1, D_1 = 0, D_k = (k-1)(D_{k-1} + D_{k-2})\)
  • 答案 = \(\binom{n}{m} \times D_{n-m}\)

时空复杂度

  • 预处理:\(O(N)\)(阶乘、逆元、错排数)
  • 每组询问:\(O(1)\)
  • 空间复杂度:\(O(N)\)

代码

#include <cstdio>
using namespace std;

const int MAXN = 1000005;
const int MOD = 1000000007;

long long fac[MAXN];      // 阶乘
long long invfac[MAXN];   // 阶乘逆元
long long d[MAXN];        // 错排数

// 快速幂
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() {
    // 预处理阶乘
    fac[0] = 1;
    for (int i = 1; i < MAXN; i++) fac[i] = fac[i - 1] * i % MOD;
    
    // 预处理阶乘逆元
    invfac[MAXN - 1] = qpow(fac[MAXN - 1], MOD - 2);
    for (int i = MAXN - 2; i >= 0; i--) {
        invfac[i] = invfac[i + 1] * (i + 1) % MOD;
    }
    
    // 预处理错排数
    d[0] = 1;
    d[1] = 0;
    for (int i = 2; i < MAXN; i++) {
        d[i] = (i - 1) * (d[i - 1] + d[i - 2]) % MOD;
    }
    
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m;
        scanf("%d%d", &n, &m);
        // C(n, m) = n! / (m! * (n-m)!)
        long long comb = fac[n] * invfac[m] % MOD * invfac[n - m] % MOD;
        long long ans = comb * d[n - m] % MOD;
        printf("%lld\n", ans);
    }
    
    return 0;
}

例8:P1313 [NOIP 2011 提高组] 计算系数

题目简意

给定多项式 \((ax + by)^k\),求展开后 \(x^n y^m\) 项的系数,对 \(10007\) 取模。

思路分析

由二项式定理:

\[(ax + by)^k = \sum_{i=0}^{k} \binom{k}{i} (ax)^i (by)^{k-i} \]

$ x^n y^m $ 项对应 \(i = n\),\(k-i = m\),系数为:

\[\binom{k}{n} \cdot a^n \cdot b^m \]

其中 \(\binom{k}{n}\) 可用杨辉三角(组合数递推)求得,\(a^n\) 和 \(b^m\) 用快速幂。

时空复杂度

  • 时间复杂度:\(O(k^2 + \log n + \log m)\)
  • 空间复杂度:\(O(k^2)\)

代码

#include <cstdio>
using namespace std;

const int MOD = 10007;

int c[1005][1005];

// 快速幂
int qpow(int a, int b) {
    int 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 a, b, k, n, m;
    scanf("%d%d%d%d%d", &a, &b, &k, &n, &m);
    
    // 杨辉三角求组合数 C(k, n)
    c[0][0] = 1;
    for (int i = 1; i <= k; i++) {
        c[i][0] = c[i][i] = 1;
        for (int j = 1; j < i; j++) {
            c[i][j] = (c[i - 1][j - 1] + c[i - 1][j]) % MOD;
        }
    }
    
    int ans = c[k][n];
    ans = ans * qpow(a, n) % MOD;
    ans = ans * qpow(b, m) % MOD;
    
    printf("%d\n", ans);
    return 0;
}

例9:CF1332E Height All the Same

题目简意

有一个 \(n \times m\) 的网格,每个格子初始高度在 \([L, R]\) 范围内任意。每次操作可以:

  1. 选择一个格子,将其高度 \(+2\)
  2. 选择两个相邻格子,将两者高度均 \(+1\)

问有多少种初始高度方案,能够通过若干次操作使所有格子高度相同。

思路分析

关键观察:结果只与高度的奇偶性有关。

  • 操作 1(\(+2\))不改变奇偶性
  • 操作 2(相邻两个 \(+1\))同时翻转两个相邻格子的奇偶性

问题转化为:能否通过翻转相邻格子的奇偶性,使整个网格奇偶性一致。

  • 若 \(n \times m\) 为奇数:任何奇偶性分布都可以调整为全奇或全偶,方案数为 \((R-L+1)^{nm}\)
  • 若 \(n \times m\) 为偶数:需要奇数个数的格子数量为偶数。总方案数为 \(\frac{(R-L+1)^{nm} + (R-L+1)^{nm/2} \times [奇偶数量相等时的调整]}{2}\)

简化结论:

  • 若 \(nm\) 为奇数:\(ans = (R-L+1)^{nm}\)
  • 若 \(nm\) 为偶数:\(ans = \frac{(R-L+1)^{nm} + (R-L+1)^{nm/2} \times (奇偶个数各半的调整)}{2}\)

时空复杂度

  • 时间复杂度:\(O(\log(nm))\)(快速幂)
  • 空间复杂度:\(O(1)\)

代码

#include <cstdio>
using namespace std;

const int MOD = 998244353;

long long qpow(long long a, long long b) {
    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() {
    long long n, m, L, R;
    scanf("%lld%lld%lld%lld", &n, &m, &L, &R);
    
    long long cnt = R - L + 1;
    long long cells = n * m;
    
    if (cells & 1) {
        // 格子总数为奇数:所有方案都可行
        printf("%lld\n", qpow(cnt, cells));
    } else {
        // 格子总数为偶数
        long long even = cnt / 2;      // 偶数高度的个数
        long long odd = cnt - even;    // 奇数高度的个数
        // 公式:(cnt^cells + (even - odd)^cells) / 2
        long long ans = (qpow(cnt, cells) + qpow((even - odd + MOD) % MOD, cells)) % MOD;
        ans = ans * qpow(2, MOD - 2) % MOD; // 乘以 2 的逆元
        printf("%lld\n", ans);
    }
    
    return 0;
}

例10:P1044 [NOIP 2003 普及组] 栈

题目简意

有 \(n\) 个数依次入栈,问有多少种不同的出栈序列。

思路分析

这是经典的卡特兰数问题。

\(n\) 个数的出栈序列数为第 \(n\) 个卡特兰数:

\[C_n = \frac{1}{n+1} \binom{2n}{n} \]

递推公式:\(C_0 = 1\),\(C_n = \sum_{i=0}^{n-1} C_i \times C_{n-1-i}\)

也可以理解为:第一个入栈的元素第 \(k\) 个出栈,则前 \(k-1\) 个和后 \(n-k\) 个独立,方案数为 \(C_{k-1} \times C_{n-k}\),求和得递推式。

时空复杂度

  • 时间复杂度:\(O(n^2)\)(递推)
  • 空间复杂度:\(O(n)\)

代码

#include <cstdio>
using namespace std;

long long catalan[20];

int main() {
    int n;
    scanf("%d", &n);
    
    catalan[0] = 1;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j < i; j++) {
            catalan[i] += catalan[j] * catalan[i - 1 - j];
        }
    }
    
    printf("%lld\n", catalan[n]);
    return 0;
}

其他题目(思维导图中未作为例题,但用户列表中包含)

以下题目在思维导图中有对应的知识点(容斥、卡特兰数、斯特林数等),但未作为例题列出,简要给出题解。


P2567 [SCOI2010] 幸运数字

题目简意

定义“幸运数字”为只包含数字 6 和 8 的数(如 6, 8, 66, 68, 86, 88...)。一个数是“近似幸运数字”当且仅当它是某个幸运数字的倍数。求区间 \([L, R]\) 内近似幸运数字的个数。

思路分析

  1. DFS 枚举所有幸运数字(\(2^{10}\) 级别)
  2. 去掉有倍数关系的幸运数字(只保留最小的那个)
  3. 容斥原理求 \([1, x]\) 内近似幸运数字个数:枚举幸运数字子集,求 LCM,用 \(x / LCM\) 得到该子集的倍数个数,奇数个加,偶数个减
  4. 答案 = \(calc(R) - calc(L-1)\)

P5505 [JSOI2011] 分特产

题目简意

有 \(n\) 个有标号的同学和 \(m\) 种特产,第 \(i\) 种特产有 \(a_i\) 个(同种特产完全相同)。将特产全部分给同学,要求每个同学至少分到一个特产,求方案数。

思路分析

容斥:至少 \(i\) 个同学没有分到特产。

  • 先选出 \(i\) 个同学为空:\(\binom{n}{i}\)
  • 剩下的 \(n-i\) 个同学分配所有特产:每种特产独立分配,第 \(j\) 种特产有 \(a_j\) 个,分给 \(n-i\) 个人的方案数为 \(\binom{a_j + n - i - 1}{n - i - 1}\)(隔板法)
  • 至少 \(i\) 个同学为空的方案数 = \(\binom{n}{i} \times \prod_{j=1}^{m} \binom{a_j + n - i - 1}{n - i - 1}\)
  • 容斥:\(ans = \sum_{i=0}^{n} (-1)^i \times \text{至少 i 个同学为空的方案数}\)

P3223 [HNOI2012] 排队

题目简意

\(n\) 个男生、\(m\) 个女生、2 个老师排队,要求任意两个女生不能相邻,两个老师不能相邻,求方案数。

思路分析

分类讨论:

  1. 先排男生和老师(把老师当特殊男生):\(n+2\) 个人排列,再插入女生
  2. 用总方案 − 老师相邻的方案

具体:

  • 先排 \(n\) 个男生:\(n!\) 种,形成 \(n+1\) 个空位
  • 插入 2 个老师:\(A_{n+1}^2\) 种(不能相邻自动满足,因为插在不同空位)
  • 再插入 \(m\) 个女生:在 \(n+3\) 个位置中选 \(m\) 个不相邻位置:\(\binom{n+3}{m} \times m!\)

但需排除老师相邻的情况,用捆绑法处理。

注意:答案可能很大,需要高精度。


P2822 [NOIP 2016 提高组] 组合数问题

题目简意

有 \(T\) 组询问,每组给定 \(n, m\),求有多少对 \((i, j)\) 满足 \(0 \le j \le i \le n\) 且 \(\binom{i}{j}\) 是 \(k\) 的倍数。

思路分析

  1. 预处理组合数 \(C[i][j]\) 对 \(k\) 取模:\(C[i][j] = (C[i-1][j-1] + C[i-1][j]) \bmod k\)
  2. 若 \(C[i][j] == 0\),则该组合数是 \(k\) 的倍数
  3. 二维前缀和 \(sum[i][j]\) 表示 \(\binom{0..i}{0..j}\) 中为 \(k\) 的倍数的个数
  4. 每组询问 \(O(1)\) 回答

P3214 [HNOI2011] 卡农

题目简意

从集合 \(\{1, 2, ..., n\}\) 的所有非空子集中选出 \(m\) 个互不相同的子集(集合之间无序),要求每个元素在所有选出的子集中出现的总次数为偶数,求方案数。

思路分析

  1. 先处理无序:先按有序计算,最后除以 \(m!\)
  2. 设 \(f[i]\) 表示选 \(i\) 个有序子集满足条件的方案数
  3. 前 \(i-1\) 个集合任意选:\(P(2^n-1, i-1)\) 种
  4. 第 \(i\) 个集合由前 \(i-1\) 个集合唯一确定(保证每个元素出现偶数次)
  5. 减去第 \(i\) 个集合为空集的情况(空集不允许)
  6. 减去第 \(i\) 个集合与前面某个集合重复的情况
  7. 递推式:\(f[i] = P(2^n-1, i-1) - f[i-1] - (i-1)(2^n - i + 1) f[i-2]\)
  8. 答案 = \(f[m] \times inv(m!)\)

P2532 [AHOI2012] 树屋阶梯

题目简意

用 \(n\) 个大小相同的矩形钢材搭建一个 \(n\) 层的阶梯形状(每层比上一层多一块),求不同搭建方案数。

思路分析

卡特兰数。

设 \(f[n]\) 为 \(n\) 层阶梯的方案数。考虑左下角最大的矩形,枚举其大小 \(i\),则:

\[f[n] = \sum_{i=0}^{n-1} f[i] \times f[n-1-i] \]

这是卡特兰数的递推式。

注意:\(n\) 最大为 \(500\),答案巨大,需要高精度。


P3200 [HNOI2009] 有趣的数列

题目简意

称一个长度为 \(2n\) 的数列 \(a_1, a_2, ..., a_{2n}\) 是“有趣的”,如果:

  1. \(a_1, a_3, ..., a_{2n-1}\)(奇数位)和 \(a_2, a_4, ..., a_{2n}\)(偶数位)都单调递增
  2. 对任意 \(i\),\(a_{2i-1} < a_{2i}\)

求有趣的数列个数。

思路分析

卡特兰数。

将 \(1 \sim 2n\) 依次填入奇数位和偶数位,奇数位在任何时刻的数量 \(\ge\) 偶数位数量,等价于 Catalan 数第 \(n\) 项。

注意:模数 \(p\) 不一定是质数,不能直接用逆元。用质因数分解计算组合数 \(\frac{1}{n+1}\binom{2n}{n}\)。


CF1278F Cards

题目简意

有一副 \(m\) 张牌的牌堆,其中一张是王牌。进行 \(n\) 次独立实验:每次洗牌后看第一张牌,若是王牌则记录。设 \(x\) 为 \(n\) 次中王牌出现在第一张的次数,求 \(E(x^k)\)。

思路分析

利用第二类斯特林数降幂:

\[i^k = \sum_{j=0}^{k} S(k, j) \times j! \times \binom{i}{j} \]

其中 \(S(k, j)\) 为第二类斯特林数。

\(i\) 服从二项分布 \(B(n, \frac{1}{m})\):

\[E(x^k) = \sum_{i=0}^{n} i^k \binom{n}{i} (\frac{1}{m})^i (1-\frac{1}{m})^{n-i} \]

代入降幂公式化简即可。


P3228 [HNOI2013] 数列

题目简意

求长度为 \(k\) 的正整数序列 \(a_1, a_2, ..., a_k\) 的个数,满足:

  1. \(1 \le a_1 \le n\)
  2. \(1 \le a_i - a_{i-1} \le m\)(\(2 \le i \le k\))

思路分析

考虑差分序列 \(b_i = a_{i+1} - a_i\)(\(1 \le i \le k-1\)),每个 \(b_i \in [1, m]\)。

对于一组确定的差分 \(b_1, ..., b_{k-1}\),\(a_1\) 的取值范围为 \([1, n - \sum b_i]\),共有 \(n - \sum b_i\) 种取值。

总方案数:

\[\sum_{b_1=1}^{m} ... \sum_{b_{k-1}=1}^{m} (n - \sum_{i=1}^{k-1} b_i) \]

化简得:

\[n \cdot m^{k-1} - (k-1) \cdot m^{k-2} \cdot \frac{m(m+1)}{2} \]


CF961G Partitions

题目简意

将 \(n\) 个有标号的元素划分为 \(k\) 个非空无标号集合(即集合划分),每个元素 \(i\) 有权值 \(w_i\)。一个划分的权值定义为 \(\sum_{S \in \text{划分}} |S| \cdot \sum_{i \in S} w_i\)。求所有划分的权值和。

思路分析

考虑每个元素的贡献。

元素 \(i\) 的贡献 = \(w_i \times\)(所有划分中,\(i\) 所在集合大小之和)

对所有元素求和:

\[ans = (\sum_{i=1}^{n} w_i) \times \sum_{j=1}^{n} \binom{n-1}{j-1} \cdot S(n-j, k-1) \cdot j \]

其中 \(S\) 为第二类斯特林数。

等价地:

\[ans = (\sum w_i) \times (S(n, k) + (n-1) \times S(n-1, k)) \]

posted @ 2026-08-24 20:42  surprise_ying  阅读(42)  评论(0)    收藏  举报