牛客2021多校第四场G(生成函数, 组合数学,计数) 题解

我最近怎么总是做生成函数的题目

题目大意

对于给定的 n , k , D n,k,D n,k,D​, 求 ∑ [ ∑ a i = D ] D ! ∏ ( a i + k ) ! \sum_{[\sum a_i = D]}\frac{D!}{\prod(a_i+k)!} [ai=D](ai+k)!D!

若答案表示为 x y \frac{x}{y} yx​ 则输出 x y   m o d   998244353 \frac{x}{y}\space mod\space 998244353 yx mod 998244353​​

其中
1 ≤ n ≤ 50 0 ≤ k ≤ 50 0 ≤ D ≤ 1 0 8 1 \leq n \leq 50 \\0\leq k\leq50 \\0\leq D \leq 10^8 1n500k500D108
我是链接

题目分析

前置芝士(结论)

∑ [ ∑ 1 n a i = D ] D ! ∏ 1 n a i ! = n D \sum_{[\sum_1^n a_i = D]} \frac{D!}{\prod_1^n a_i!} = n^D [1nai=D]1nai!D!=nD

这个结论理解为在 [ 1.. n ] [1..n] [1..n]​​中选数, 组成长度为 D D D​​的数列的方案数, a i a_i ai​ 表示 i i i在这个数列中出现的次数

D ! D! D!​​​是全排列, 除掉的 ∏ a i ! \prod a_i! ai!​​​是对于每一个数的重复方案数, D ! ∏ 1 n a i \frac{D!}{\prod_1^n a_i} 1naiD!​​​是对于一个特定数列 a i a_i ai​​​的产生的方案数,求和便得到了所有组合的方案数。所有组合的方案数必定是每一位都是 n n n​种可能性,所以求和得到了 n D n^D nD

因此可以得知
∑ [ ∑ 1 n a i = D ] 1 ∏ 1 n a i ! = n D D ! \sum_{[\sum_1^n a_i = D]} \frac{1}{\prod_1^n a_i!} = \frac{n^D}{D!} [1nai=D]1nai!1=D!nD
而这个问题使用生成函数解释为

构造指数型母函数 g k ( x ) = ∑ 0 ∞ x i i ! = e x g_k(x) = \sum_0^\infty \frac{x^i}{i!} = e^x gk(x)=0i!xi=ex​​表示对于第 k k k​个数字选取了 i i i

那么总共的方案数就有 f ( x ) = ∏ 1 n g k ( x ) f(x) = \prod_1^n g_k(x) f(x)=1ngk(x)其中第 D D D项的系数就表示从 [ 1.. n ] [1..n] [1..n]中生成序列的方案数,就是上文中的 n D D ! \frac{n^D}{D!} D!nD

所以 f ( x ) = ( e x ) n f(x) = (e^x)^n f(x)=(ex)n

f ( x ) f(x) f(x)的第 D D D项的系数为 n D D ! \frac{n^D}{D!} D!nD

正经分析

本题题目可以改写为
∑ [ ∑ a i = D + n k ,   a i > k ] D ! ∏ ( a i ) ! = D !   ∗ ∑ [ ∑ a i = D + n k ,   a i > k ] 1 ∏ ( a i ) ! \sum_{[\sum a_i = D+nk, \space a_i > k]}\frac{D!}{\prod(a_i)!} \\=D!\space *\sum_{[\sum a_i = D+nk, \space a_i > k]}\frac{1}{\prod(a_i)!} [ai=D+nk, ai>k](ai)!D!=D! [ai=D+nk, ai>k](ai)!1
发现将 D ! D! D!​​提出后剩下的部分由上文的解释方法为从 [ 1.. n ] [1..n] [1..n]​​中选取数字,每个数字至少选取 k k k​​次,生成总长度为 D + n k D+nk D+nk​​的序列,求总方案数

所以这部分使用生成函数来解释
g k ( x ) = ∑ k ∞ x i i ! = e x − ∑ 0 k − 1 x i i ! f ( x ) = ∏ 1 n g k ( x ) = ( e x − ∑ 0 k − 1 x i i ! ) n f ( x ) = e n x − ( n 1 ) e ( n − 1 ) x ( ∑ 0 k − 1 x i i ! ) + . . . = ∑ 0 n ( − 1 ) i ( n i ) e ( n − i ) x ( ∑ 0 k − 1 x j j ! ) i g_k(x) = \sum_k^\infty \frac{x^i}{i!} = e^x - \sum_0^{k-1}\frac{x^i}{i!} \\f(x) = \prod_1^ng_k(x) = (e^x - \sum_0^{k-1}\frac{x^i}{i!})^n \\f(x) = e^{nx} - \binom{n}{1}e^{(n-1)x}(\sum_0^{k-1}\frac{x^i}{i!})+... \\=\sum_0^n(-1)^i\binom{n}{i}e^{(n-i)x}(\sum_0^{k-1}\frac{x^j}{j!})^i gk(x)=ki!xi=ex0k1i!xif(x)=1ngk(x)=(ex0k1i!xi)nf(x)=enx(1n)e(n1)x(0k1i!xi)+...=0n(1)i(in)e(ni)x(0k1j!xj)i
剩下的部分就是 f ( x ) f(x) f(x) x D + n k x^{D+nk} xD+nk处的系数

所以怎么才能求出这个系数 S S S

如果 ( ∑ 0 k − 1 x j j ! ) i (\sum_0^{k-1}\frac{x^j}{j!})^i (0k1j!xj)i​​​​​展开的各项系数为 K i , j K_{i,j} Ki,j​​​​​​, 而 e ( n − i ) x e^{(n-i)x} e(ni)x​在 j j j处的系数为 S i , j S_{i,j} Si,j

那么
S = ∑ 0 n ( − 1 ) i ( n i ) ( ∑ 0 i ( k − 1 ) S i , D + n k − j K i , j ) S = \sum_0^n(-1)^i\binom{n}{i}(\sum_0^{i(k-1)} S_{i,D+nk-j}K_{i,j}) S=0n(1)i(in)(0i(k1)Si,D+nkjKi,j)
我们发现 S i , j = ( n − i ) j j ! S_{i,j} = \frac{(n-i)^j}{j!} Si,j=j!(ni)j 也就是说 S i , D + n k − j = ( n − i ) ( D + n k − j ) ( D + n k − j ) ! S_{i,D+nk-j} = \frac{(n-i)^{(D+nk-j)}}{(D+nk-j)!} Si,D+nkj=(D+nkj)!(ni)(D+nkj)

K i , j K_{i,j} Ki,j可以由递推的方式求出
( K i − 1 , 0 + K i − 1 , 1 x + . . . + K i − 1 , i ( k − 1 ) x i ( k − 1 ) ) ( ∑ 0 k − 1 x j j ! ) ∴ K i , j = ∑ x = 0 k − 1 K i − 1 , k − x ∗ 1 x ! , j ∈ [ 0 , i ( k − 1 ) ] (K_{i-1,0}+K_{i-1,1}x+...+K_{i-1, i(k-1)}x^{i(k-1)})(\sum_0^{k-1}\frac{x^j}{j!}) \\\therefore K_{i,j}=\sum_{x=0}^{k-1}K_{i-1,k-x}*\frac{1}{x!}, j \in [0, i(k-1)] (Ki1,0+Ki1,1x+...+Ki1,i(k1)xi(k1))(0k1j!xj)Ki,j=x=0k1Ki1,kxx!1,j[0,i(k1)]
有了递推式,就可以使用DP的方式求出 K i , j K_{i,j} Ki,j

可是这个题目并没有结束,1e8的范围求若干次阶乘,时间复杂度无法接受,因此将 D ! D! D!​​乘进去,与 S i , D + n k − j S_{i,D+nk-j} Si,D+nkj​​结合, 得到 ( n − i ) ( D + n k − j ) D ! ( D + n k − j ) ! \frac{(n-i)^{(D+nk-j)}D!}{(D+nk-j)!} (D+nkj)!(ni)(D+nkj)D!​将 D ! ( D + n k − j ) ! \frac{D!}{(D+nk-j)!} (D+nkj)!D!结合计算,发现每次最多只需要计算 n k nk nk​个数相乘,时间变得非常充裕

综上
A N S = D ! ∗ S = ∑ 0 n ( − 1 ) i ( n i ) ( ∑ 0 i ( k − 1 ) ( n − i ) D + n k − j D ! ( D + n k − j ) ! K i , j ) ANS = D!*S = \sum_0^n(-1)^i\binom{n}{i}(\sum_0^{i(k-1)} (n-i)^{D+nk-j}\frac{D!}{(D+nk-j)!}K_{i,j}) ANS=D!S=0n(1)i(in)(0i(k1)(ni)D+nkj(D+nkj)!D!Ki,j)

由此我们解决了求 S S S的若干问题,DP求 K K K的复杂度为 O ( ( n k ) 2 ) O((nk)^2) O((nk)2) 最后统计答案的时间复杂度为 O ( n 2 k ) O(n^2k) O(n2k)

所以最后的时间复杂度为 O ( ( n k ) 2 ) O((nk)^2) O((nk)2)

通过了这个题目

通过代码

人比较菜,代码比较丑,dalao勿喷

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 50;
const int MOD = 998244353;
typedef long long ll;
ll dp[MAXN + 10][MAXN * MAXN + 10];
ll qp(ll a, int b)
{
    ll ans = 1;
    while(b)
    {
        if(b & 1)
            ans = (ans * a) % MOD;
        b >>= 1;
        a = (a  * a) % MOD;
    }
    return ans;
}
ll inv(ll a)
{
    return qp(a, MOD - 2);
}
ll fac[50 * 50 + 100];
ll facInv[50 * 50 + 100];
ll facDInv[50 * 50 + 100];
ll C(int n, int m)
{
    return (((fac[n] * facInv[m]) % MOD) * facInv[n - m]) % MOD;
}
int main()
{
    int n,k,d;
    scanf("%d%d%d", &n, &k, &d);
    
    fac[0] = 1;
    for(int i = 1; i <= max(n * k, max(n, k)); ++i)
        fac[i] = (fac[i - 1] * i) % MOD;
    for(int i = 0; i <= max(n * k, max(n, k)); ++i)
        facInv[i] = inv(fac[i]);
    //dp 用来计算$$(e^x-\sum{i=0}{k-1}\frac{x^i}{i!})^n$$的各项系数中非e^?的部分
    dp[0][0] = 1;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 0; j <= i * (k - 1); ++j)
        {
            for(int x = 0; x <= min(j, k - 1); ++x)
            {
                dp[i][j] += (dp[i - 1][j - x] * facInv[x]) % MOD;
                dp[i][j] %= MOD;
            }
            
        }
    }
    facDInv[0] = 1;
    for(int i = 1; i <= n * k; ++i)
        facDInv[i] = (facDInv[i - 1] * ((d + i) % MOD)) % MOD; 
    for(int i = 1; i <= n * k; ++i)
        facDInv[i] = inv(facDInv[i]);
    ll ans = 0;
    int sgn = -1;
    for(int i = 0; i <= n; ++i)
    {
        sgn *= -1;
        ll tmp = 0;
        for(int j = 0; j <= i * (k - 1); ++j)
        {
            tmp +=(((dp[i][j] * qp(n - i, d + n * k - j)) % MOD) * facDInv[n * k - j]) % MOD;
            tmp %= MOD;
        }
        tmp *= C(n, i);
        tmp %= MOD;
        tmp *= sgn;
        tmp %= MOD;
        ans += tmp;
        ans %= MOD;
    }
    ans += MOD;
    ans %= MOD;
    printf("%lld\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值