2021牛客暑期多校训练营4 G题: Product

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 ! ∏ i = 1 n ( a i + k ) ! ( ∀ ∈ [ 1 , n ] , a i ≥ 0 ; ∑ i = 1 n a i = D ) \sum\frac{D!}{\prod\limits_{i=1}^{n}(a_i+k)!}(\forall\in[1,n],a_i \ge 0;\sum\limits_{i=1}^{n}a_i=D) i=1n(ai+k)!D!([1,n],ai0;i=1nai=D) 并不方便。
我们先舍去 a i + k a_i+k ai+k 中的 + k +k +k ,考虑① ∑ D ! ∏ i = 1 n a i ! ( ∀ ∈ [ 1 , n ] , a i ≥ 0 ; ∑ i = 1 n a i = D ) \sum\frac{D!}{\prod\limits_{i=1}^{n}a_i!}(\forall\in[1,n],a_i \ge 0;\sum\limits_{i=1}^{n}a_i=D) i=1nai!D!([1,n],ai0;i=1nai=D) 类型的权值。
我们将该种类型的权值展开,写作 ∑ D ! a 1 ! a 2 ! . . . a n ! \sum\frac{D!}{a_1!a_2!...a_n!} a1!a2!...an!D! ,因为 ∑ i = 1 n a i = D \sum\limits_{i=1}^{n}a_i=D i=1nai=D ,所以该类型权值可以转化为组合数含义:共有 D D D 个不同的球,分为 n n n 组,求总方案数。显然,对于每个球有 n n n 个分组去向,所以总方案数应为 n D n^D nD
然后我们考虑将 ( a i + k ) (a_i+k) (ai+k) 中的 + k +k +k 并入 a i a_i ai 中,原式改写为② ∑ D ! ∏ i = 1 n a i ! ( ∀ ∈ [ 1 , n ] , a i ≥ k ; ∑ i = 1 n a i = D + n k ) \sum\frac{D!}{\prod\limits_{i=1}^{n}a_i!}(\forall\in[1,n],a_i \ge k;\sum\limits_{i=1}^{n}a_i=D+nk) i=1nai!D!([1,n],aik;i=1nai=D+nk) (注意条件有改变,每一个 a i a_i ai 加上 k k k 后应满足 a i ≥ k a_i\ge k aik ,同时总和加上 n k nk nk 变为 D + n k D+nk D+nk )。
直接考虑②较不方便,我们先尝试计算 a i ≥ 0 a_i\ge 0 ai0 条件的总方案数(合法方案数+非法方案数),即③ ∑ D ! ∏ i = 1 n a i ! ( ∀ ∈ [ 1 , n ] , a i ≥ 0 ; ∑ i = 1 n a i = D + n k ) \sum\frac{D!}{\prod\limits_{i=1}^{n}a_i!}(\forall\in[1,n],a_i \ge 0;\sum\limits_{i=1}^{n}a_i=D+nk) i=1nai!D!([1,n],ai0;i=1nai=D+nk)
对于③,我们考虑将其转化为类似①的形式如下: ∑ ( D + n k ) ! a 1 ! a 2 ! . . . a n ( ∀ ∈ [ 1 , n ] , a i ≥ 0 ; ∑ i = 1 n a i = D + n k ) \sum\frac{(D+nk)!}{a_1!a_2!...a_n}(\forall\in[1,n],a_i \ge 0;\sum\limits_{i=1}^{n}a_i=D+nk) a1!a2!...an(D+nk)!([1,n],ai0;i=1nai=D+nk) ,显然该式与③并不对等(分母处 D ! − > ( D + n k ) ! D!->(D+nk)! D!>(D+nk)! ),我们在其前乘上修正系数 D ! ( D + n k ) ! \frac{D!}{(D+nk)!} (D+nk)!D! 使得与原式一致,即 D ! ( D + n k ) ! ∑ ( D + n k ) ! a 1 ! a 2 ! . . . a n ( ∀ ∈ [ 1 , n ] , a i ≥ 0 ; ∑ i = 1 n a i = D + n k ) \frac{D!}{(D+nk)!}\sum\frac{(D+nk)!}{a_1!a_2!...a_n}(\forall\in[1,n],a_i \ge 0;\sum\limits_{i=1}^{n}a_i=D+nk) (D+nk)!D!a1!a2!...an(D+nk)!([1,n],ai0;i=1nai=D+nk) ,用类似①的方法化简得 D ! ( D + n k ) ! n D + n k \frac{D!}{(D+nk)!}n^{D+nk} (D+nk)!D!nD+nk
然后我们考虑筛去那些存在 a i < k a_i<k ai<k 的非法方案数,我们首先通过动态规划初始化,设 d p i , j dp_{i,j} dpi,j 表示组合数含义 j j j 个球分为 i i i 个非法组(即每组球数均少于 k k k ) ,那么我们通过枚举最后一个非法组的球数(同时需考虑球的分配情况有多种,即转移式中的组合项 C C C ),易得转移式:
d p i , j = ∑ s = 0 k − 1 d p i − 1 , j − s C j s dp_{i,j}=\sum^{k-1}_{s=0}dp_{i-1,j-s}C^s_j dpi,j=s=0k1dpi1,jsCjs

那么我们可以通过容斥原理来筛掉这些解,结合③的变式,我们将②最终变形为解(记得考虑球的分布情况,乘上组合项 C C C ):
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 ( 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}C^i_nC^j_{D+nk}(n-i)^{D+nk-j} (D+nk)!D!i=0n(1)ij=0i(k1)dpi,jCniCD+nkj(ni)D+nkj

下图为导图式推理,并带上更详细的注释(?):
在这里插入图片描述

参考代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=55,mod=998244353;
ll n,k,D,C[N*N][N+1],dp[N][N*N],sum,facC,CC,ans;
ll powmod(ll x,ll p){ll ret=1;while(p){if(p&1)ret=ret*x%mod;x=x*x%mod;p>>=1;}return ret;}//快速幂
ll inv(ll x){return powmod(x,mod-2);}//求逆元
int main()
{
	std::ios::sync_with_stdio(false),cin.tie(0);
	ll i,j,l,o;
	cin>>n>>k>>D;
	o=N*N;
	for(i=0;i<=o;i++)C[i][0]=1;
	for(i=1;i<=o;i++){
		l=min(N,i);
		for(j=1;j<=l;j++){
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;//组合数打表
		}
	}
	dp[0][0]=1;//dp初始化,0个球分0个非法组有1种方案
	for(i=0;i<n;i++){
		l=i*(k-1);
		for(j=0;j<=l;j++){
			for(o=0;o<k;o++)dp[i+1][j+o]=(dp[i+1][j+o]+dp[i][j]*C[j+o][o])%mod;//优化递推计算dp
		}
	}
	for(i=0;i<=n;i++){
		sum=0;
		CC=(i&1?mod-C[n][i]:C[n][i]);
		l=i*(k-1);
		facC=1;
		for(j=0;j<=l;j++){
			sum=(sum+CC*powmod(n-i,D+n*k-j)%mod*dp[i][j]%mod*facC%mod)%mod;
			facC=facC*(D+n*k-j)%mod*inv(j+1)%mod;//保存C(D+nk,j)项的值,该项的n过大不适用打表
		}
        ans=(ans+sum)%mod;
	}
	for(i=D+n*k;i>D;i--)ans=ans*inv(i)%mod;//乘上修正系数,省去了分子与分母的重合部分
	cout<<ans<<endl;
	return 0;
}
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值