AT_abc405_e 题解

题目传送门:
洛谷
AT_coder

题目概述:

你有 \(A\) 个苹果,\(B\) 个橙子,\(C\) 串香蕉,\(D\) 串葡萄。同种水果之间是完全相同的。

求把它们从左往右排成一排的方案数,方案需要满足:

  • 所有的苹果都在所有的香蕉的左边;
  • 所有的苹果都在所有的葡萄的左边;
  • 所有的橙子都在所有的葡萄的左边。

答案对 \(998244353\) 取模。

思路:

首先枚举第一个香蕉前面有多少个橘子。
对于每一种数量,要利用隔板法求一下两个值:
1、求出香蕉前面 苹果和橘子混在一起的方案数
2、后面剩下的橙子和葡萄拼起来,中间混入所有香蕉的方案数。

由于每一种合法方案都可以由两个符合条件(上述两个)组成,故可以利用乘法原理求出香蕉前面有i个橘子的合法方案数。

注意事项:

1、因为在第2段中橘子可能会出现在香蕉前面,所以为了区分,我们规定在第1段中必须满足最后一个是苹果。
2、由于我们需要用到排列组合,而排列组合时数字较大且次数较多,因此我们需要利用逆元,还需要准备两个数组,一个求\(n\)的阶乘模\(998244353\), 一个求\(n\)的逆元。
逆元求法:

f[maxn] = qp(fa[maxn], mod - 2);
for (int i = maxn - 1; i >= 0; i--)
{
    f[i] = f[i + 1] * (i + 1) % mod;
}

3、预备数组至少要开到\(3 \times 10^6\) ,因为d+(b-i)+c的上限可以达到 \(3 \times 10^6\)(我因此赛后错了5次,故此警戒)

代码:

#include <iostream>
#define mod 998244353
using namespace std;
long long f[3000001]; // 逆元数组
long long fa[3000001]; // 阶乘数组
long long qp(long long a, long long b) // 快速幂(下面准备逆元数组时要用)
{
    long long s = 1;
    while (b)
    {
        if (b % 2 == 1)
        {
            s = (s * a) % mod;
        }
        b /= 2;
        a = (a * a) % mod;
    }
    return s;
}
long long C(int n, int m)
{
    if (m == 0) return 1;
    //     n!
    // -----------
    //   m!(n-m)!
    return fa[n] * f[m] % mod * f[n - m] % mod; // 利用逆元以及阶乘直接算出答案
}
int main()
{

    int a, b, c, d;
    cin >> a >> b >> c >> d;
    long long ans = 0;
    long long s = 1;
    // 准备阶乘取模数组
    fa[0] = 1;
    for (int k = 1; k <= 3000000; k++)
    {
        fa[k] = fa[k - 1] * k % mod;
    }
    // 准备逆元数组
    f[3000000] = qp(fa[3000000], mod - 2);
    for (int i = 2999999; i >= 0; i--)
    {
        f[i] = f[i + 1] * (i + 1) % mod;
    }
    for (int i = 0; i <= b; i++) // 枚举香蕉前面有多少个橘子
    {
        long long t1 = C(i + a - 1, i) % mod; // 第1段,注意需要 - 1,具体原因见注意事项
        long long t2 = C(d + (b - i) + c, c) % mod; // 第2段
        ans += t1 * t2 % mod;  // 乘法原理
        ans %= mod; // 再次取模,因为几个 < mod的数字加起来可能 > mod
    }
    cout << ans << endl;
    return 0;
}

posted @ 2025-05-13 23:39  MichaelZeng  阅读(48)  评论(0)    收藏  举报