题目链接
题目大意
给定三个整数
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(1≤n,k≤50,0≤D≤108)。
定义非负整数序列
a
a
a的权值为
D
!
∏
i
=
1
n
(
a
i
+
k
)
!
\frac{D!}{\prod\limits_{i=1}^{n}(a_i+k)!}
i=1∏n(ai+k)!D!
求所有满足以下条件的非负整数序列
a
a
a的权值和:
1.
∀
∈
[
1
,
n
]
,
a
i
≥
0
\forall\in[1,n],a_i \ge 0
∀∈[1,n],ai≥0
2.
∑
i
=
1
n
a
i
=
D
\sum_{i=1}^{n}a_i=D
∑i=1nai=D
答案对
998244353
998244353
998244353取模。
题解
考虑以下题目给的公式,如果把
k
k
k去掉则原式变成
D
!
∏
i
=
1
n
(
a
i
)
!
\frac{D!}{\prod\limits_{i=1}^{n}(a_i)!}
i=1∏n(ai)!D!
可以看出它是一个排列组合,表示为D个不同的球分解成n个组,每次取
a
i
a_i
ai个球,求可能的方案数,易得解为
n
D
n^D
nD。于是将原式中的
k
k
k合并。则原式还可以变为
D
!
∏
i
=
1
n
a
i
!
(
a
i
≥
k
&
&
∑
i
=
1
n
a
i
=
D
+
n
k
)
\frac{D!}{\prod\limits_{i=1}^{n}a_i!}(a_i \ge k \ \&\& \ \sum_{i=1}^{n}a_i=D+nk)
i=1∏nai!D!(ai≥k && i=1∑nai=D+nk)
然后我们想将
a
i
≥
k
a_i \ge k
ai≥k变为
a
i
≥
0
a_i \ge 0
ai≥0,因为在
a
i
≥
0
a_i \ge 0
ai≥0时的情况可以套用上面的。即
D
!
∏
i
=
1
n
a
i
!
=
D
!
(
D
+
n
k
)
!
∑
i
=
1
n
(
D
+
n
k
)
!
∏
a
i
!
(
a
i
≥
0
&
&
∑
i
=
1
n
a
i
=
D
+
n
k
)
\frac{D!}{\prod\limits_{i=1}^{n}a_i!}=\frac{D!}{(D+nk)!}\sum_{i=1}^{n}\frac{(D+nk)!}{\prod{a_i!}}(a_i \ge 0 \ \&\& \ \sum_{i=1}^{n}a_i=D+nk)
i=1∏nai!D!=(D+nk)!D!i=1∑n∏ai!(D+nk)!(ai≥0 && i=1∑nai=D+nk)
经化简得
s
=
D
!
(
D
+
n
k
)
!
n
D
+
n
k
s=\frac{D!}{(D+nk)!}n^{D+nk}
s=(D+nk)!D!nD+nk
然后我们要用求得的综合减掉不符合要求的综合得到真正答案。这里需要套入容斥原理(详见百度,自行学习)从上面可得
a
i
≤
k
a_i \leq k
ai≤k是不符合条件的,所以我们可以得到答案
a
n
s
ans
ans为
s
s
s减去奇数时总数的
a
i
≤
k
a_i \leq k
ai≤k,加上偶数时的答案。
为解决问题,我们定义
d
p
i
,
j
dp_{i,j}
dpi,j表示将j个不同的分解为i组,每组个数
<
k
<k
<k的方案数。
所以得到答案
D
!
(
D
+
n
k
)
(
总
值
)
∑
i
=
0
n
(
−
1
)
i
(
判
断
正
负
性
定
义
加
减
)
∑
j
=
0
i
(
k
−
1
)
(
第
i
个
非
法
组
最
多
有
i
(
k
−
1
)
个
)
C
i
n
(
组
数
)
∗
C
D
+
n
k
j
(
球
数
)
∗
d
p
i
,
j
∗
(
n
−
i
)
D
+
n
k
−
j
(
合
法
情
况
D
+
n
k
−
j
个
分
为
n
−
i
组
时
的
情
况
)
\frac{D!}{(D+nk)}(总值)\sum_{i=0}^{n}(-1)^i(判断正负性定义加减)\sum_{j=0}^{i(k-1)}(第i个非法组最多有i(k-1)个)C_{i}^{n}(组数)*C_{D+nk}^{j}(球数)*dp_{i,j}*(n-i)^{D+nk-j}(合法情况D+nk-j个分为n-i组时的情况)
(D+nk)D!(总值)i=0∑n(−1)i(判断正负性定义加减)j=0∑i(k−1)(第i个非法组最多有i(k−1)个)Cin(组数)∗CD+nkj(球数)∗dpi,j∗(n−i)D+nk−j(合法情况D+nk−j个分为n−i组时的情况)
注意全程取模
m
o
d
=
998244353
mod=998244353
mod=998244353防炸,用费马小定理进行逆元处理,快速幂加快速度。
参考代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=55,mod=998244353;
ll n,k,D,c[N*N+1][N*N+1],dp[N*N+1][N*N+1];
ll pow(ll a,ll b)
{
ll ret=1;
while(b)
{
if(b&1)
ret=ret*a%mod;
a=a*a%mod;
b>>=1;
}
return ret;
}
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,1ll*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)%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 yk=1;
for(int j=0;j<=i*(k-1);j++)
{
int w=(i&1?mod-c[n][i]:c[n][i]);
w=w*pow(n-i,D-j)%mod*dp[i][j]%mod*yk%mod;
ans=(ans+w)%mod;
yk=yk*(D-j)%mod*pow(1ll*j+1,mod-2)%mod;
}
}
for(int i=D-n*k+1;i<=D;i++)
ans=ans*pow(1ll*i,mod-2)%mod;
printf("%lld\n",ans);
}
总结
代码我参考了逆十字的(人家27分钟的时候做完。。。),这是一道排列组合+DP的题目,草稿纸上的操作比代码版上的多多了。。。