2021牛客暑期多校训练营4 G-Product(组合意义+容斥原理)

G-Product

Spy_Savior题解
Ultraman-Ace题解
组合意义+容斥原理
转化后题目关键要求 D + n k D+nk D+nk个小球放在 n n n个盒子中,每个盒子至少有k个小球的方案数。

题目的等价式子为
∑ ∑ i = 1 n a i = D + n k [ a i ≥ k ] D ! ∏ i = 1 n a i ! \sum_{\sum_{i=1}^na_i=D+nk[a_i\ge k]}\frac{D!}{\prod_{i=1}^na_i!} i=1nai=D+nk[aik]i=1nai!D!
如果没有至少有k个小球的限制,那么根据组合意义
∑ ∑ i = 1 n a i = D + n k [ a i ≥ 0 ] ( D + n k ) ! ∏ i = 1 n a i ! = n D + n k \sum_{\sum_{i=1}^{n}a_i=D+nk[a_i\ge 0]}\frac{(D+nk)!}{\prod_{i=1}^{n}a_i!}=n^{D+nk} i=1nai=D+nk[ai0]i=1nai!(D+nk)!=nD+nk

可得
∑ ∑ i = 1 n a i = D + n k [ a i ≥ 0 ] D ! ∏ i = 1 n a i ! = D ! ( D + n k ) ! n D + n k \sum_{\sum_{i=1}^na_i=D+nk[a_i\ge 0]}\frac{D!}{\prod_{i=1}^na_i!}=\frac{D!}{(D+nk)!}n^{D+nk} i=1nai=D+nk[ai0]i=1nai!D!=(D+nk)!D!nD+nk

关键是有了限制,考虑容斥原理

dp i , j \text{dp}_{i,j} dpi,j表示 j j j个球分到 i i i个非法组的方案数,枚举最后一个非法组的球数 j j j个求的分配情况即可

dp i , j = ∑ t = 0 k − 1 dp i − 1 , j − t ( j t ) \text{dp}_{i,j}=\sum_{t=0}^{k-1}\text{dp}_{i-1,j-t}\binom{j}{t} dpi,j=t=0k1dpi1,jt(tj)


容斥原理,设时间 A i A_i Ai表示第 i i i个组不合法,那么
∣ A 1 ˉ ⋂ A 2 ˉ ⋂ ⋯ ⋂ A n ˉ ∣ = ∑ i = 0 n { ( − 1 ) n ∑ j , k ∣ A j ∣ ⋃ ⋯ ⋃ ∣ A k ∣ } |\bar{A_1}\bigcap\bar{A_2}\bigcap\dots \bigcap\bar {A_n}|=\sum_{i=0}^{n}\{(-1)^n\sum_{j,k}|A_j| \bigcup\dots\bigcup|A_k|\} A1ˉA2ˉAnˉ=i=0n{(1)nj,kAjAk}

而对于其他组就没有限制条件,可以带入上述公式可以直接求得。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
template <class T=int> T rd()
{
    T res=0;T fg=1;
    char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') fg=-1;ch=getchar();}
    while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
    return res*fg;
}
const ll mod=998244353;
int n,k;
ll D,C[2505][2505],dp[55][2505];
ll qmi(ll a,ll b)
{
    ll v=1;
    while(b)
    {
        if(b&1) v=v*a%mod;
        b>>=1;
        a=a*a%mod;
    }
    return v;
}
void init()
{
    for(int i=0;i<=2500;i++)
        for(int j=0;j<=i;j++) 
            C[i][j]=(j?C[i-1][j]+C[i-1][j-1]: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)%mod;
}
int main()
{   
    n=rd(),k=rd(),D=rd<ll>();
    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*qmi(n-i,D-j)%mod*dp[i][j]%mod*v%mod;
            ans=(ans+num)%mod;
            v=v*(D-j)%mod*qmi(j+1,mod-2)%mod;
            
        }
    }
    for(int i=D-n*k+1;i<=D;i++) ans=ans*qmi(i,mod-2)%mod;
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值