2021牛客多校#4 G-Product

原题链接
				https://ac.nowcoder.com/acm/contest/11255/G

题目大意

给定三个整数 n , k , D ( 1 ≤ n , k ≤ 50 , 0 ≤ D ≤ 1 0 8 ) n,k,D(1 \leq n,k \leq 50,0 \leq D \leq 10^8) n,k,D(1n,k50,0D108)
定义非负整数序列 a a a的权值为 D ! ∏ i = 1 n ( a i + k ) ! \frac{D!}{\prod\limits_{i=1}^{n}(a_i+k)!} i=1n(ai+k)!D!
求所有满足以下条件的非负整数序列 a a a的权值和:
1. ∀ ∈ [ 1 , n ] , a i ≥ 0 \forall\in[1,n],a_i \ge 0 [1,n],ai0
2. ∑ i = 1 n a i = D \sum_{i=1}^{n}a_i=D i=1nai=D
答案对 998244353 998244353 998244353取模。

题解

我们可以把题目转化成把 D D D个小球分成 n n n组,每组 a i a_i ai个球。

①我们先不考虑 + k +k +k的情况,将 D D D个不同的求分为 n n n组,每个球有 n n n种分组可能,总共方案数为 n D n^D nD
∑ a i ≥ 0 , ∑ i = 1 n a i = D D ! ∏ i = 1 n a i ! \sum^{}_{a_i≥0,\sum\limits^{n}_{i=1}a_i=D}\frac{D!}{\prod\limits_{i=1}^{n}a_i!} ai0,i=1nai=Di=1nai!D!
( D + n k ) (D+nk) (D+nk)个球分 n n n组的总方案书为 n D + n k n^{D+nk} nD+nk

②将 + k +k +k合并入 a i a_i ai中:
∑ a i ≥ k , ∑ i = 1 n a i = D + n k D ! ∏ i = 1 n a i ! \sum^{}_{ai≥k,\sum\limits^{n}_{i=1}a_i=D+nk}\frac{D!}{\prod^{n}_{i=1}a_i!} aik,i=1nai=D+nki=1nai!D!

将②中 a i ≥ k a_i≥k aik换成 a i ≥ 0 a_i≥0 ai0,与①可推得总方案数(包括不合理方案):
D ! ( D + n k ) ! n D + n k \frac{D!}{(D+nk)!}n^{D+nk} (D+nk)!D!nD+nk

d p i , j dp_{i,j} dpi,j表示 j j j个不同的求分为 i i i个非法组(即魅族的球数都小于 k k k),通过美剧最后一个非法组的球数与球的分配情况,得转移式:
d p i , j = ∑ t = 0 k − 1 d p i − 1 , j − t C j t dp_{i,j}=\sum^{k-1}_{t=0}dp_{i-1,j-t}C_{j}^{t} dpi,j=t=0k1dpi1,jtCjt

通过计算,得出最终转移式:
D ! ( D + n k ) ! ∑ i = 0 n ( − 1 ) i ∑ j = 0 i ( k − 1 ) d p i , j × C n i × C D + n k j × d p i , j ( n − i ) D + n k − j \frac{D!}{(D+nk)}!\sum^{n}_{i=0}(-1)^i\sum^{i(k-1)}_{j=0}dp_{i,j}\times C^{i}_{n}\times C^{j}_{D+nk}\times dp_{i,j}(n-i)^{D+nk-j} (D+nk)D!!i=0n(1)ij=0i(k1)dpi,j×Cni×CD+nkj×dpi,j(ni)D+nkj

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=55;
const int MOD=998244353;
ll powmod(ll a,ll b)
{
    ll ret=1;
    while(b)
    {
        if(b&1)ret=ret*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return ret;
}
ll C[N*N][N*N],dp[N][N*N];
ll n,k,D;
void init()
{
    for(int i=0;i<N*N;i++)
        C[i][0]=1;
    for(int i=1;i<N*N;i++)
        for(int j=1;j<=min(N,i);j++)
            C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
    dp[0][0]=1;
    for(int i=0;i<n;i++)
        for(int j=0;j<=i*(k-1);j++)
            for(int t=0;t<k;t++)
                dp[i+1][j+t]=(dp[i+1][j+t]+dp[i][j]*C[j+t][t])%MOD;
}
int main()
{
    scanf("%lld%lld%lld",&n,&k,&D);
    init();
    D+=n*k;
    ll ans=0;
    for(int i=0;i<=n;i++)
    {
        ll v=1;
        for(int j=0;j<=i*(k-1);j++)
        {
            ll num=(i&1?MOD-C[n][i]:C[n][i]);
            num=num*powmod(n-i,D-j)%MOD*dp[i][j]%MOD*v%MOD;
            ans=(ans+num)%MOD;
            v=v*(D-j)%MOD*powmod(j+1,MOD-2)%MOD;
        }
    }
    for(int i=D-n*k+1;i<=D;i++)
        ans=ans*powmod(i,MOD-2)%MOD;
    cout<<ans;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值