Problem\mathrm{Problem}Problem
Solution\mathrm{Solution}Solution
前置芝士:求解 ∑ai=N\sum a_i=N∑ai=N 的方案数
我们考虑DP,设 fi,jf_{i,j}fi,j 表示和为 iii ,数字个数为 jjj 的方案数。
我们需要让它你能重复,因此我们的转移必须从小到大或从大到小来转移。
我们考虑从大到小转移,那么若当前的最小数字不是1,我们可以考虑每一个数字都减1,对应的方案数是 fi−j,jf_{i-j,j}fi−j,j ,否则方案数是 fi−1,j−1f_{i-1,j-1}fi−1,j−1,因此就有递推式为:fi,j=fi−1,j−1+fi−j,jf_{i,j}=f_{i-1,j-1}+f_{i-j,j}fi,j=fi−1,j−1+fi−j,j
考虑一个数 xxx 在某一个方案里面出现了 iii 次的方案数,设方案数为 ttt,那么当前的贡献则为xm⋅t⋅ix^m·t·ixm⋅t⋅i。考虑如何利用上面的数组。
- 出现了至少 iii 次的方案数为 fn−xi,k−if_{n-xi,k-i}fn−xi,k−i.
- 则出现了刚好 iii 次的方案数为 fn−xi,k−i−fn−xi−x,k−i−1f_{n-xi,k-i}-f_{n-xi-x,k-i-1}fn−xi,k−i−fn−xi−x,k−i−1,差分即可。
Code\mathrm{Code}Code
#include <bits/stdc++.h>
using namespace std;
const int P = 998244353;
int n, m, k, res;
int Now[100], Val[100];
inline void Dfs(int x, int sum, int val, int last)
{
if (x == k + 1)
{
if (sum < n) return;
res = (res + val) % P;
return;
}
for (register int i=last;i<=n;++i)
if (sum + i <= n) Now[x] = i, Dfs(x+1, (sum + i) % P, (val + Val[i]) % P, i);
return;
}
inline int power(int a, int b) {
int res = 1;
while (b > 0) {
if (b & 1) res = 1LL * res * a % P;
a = 1LL * a * a % P, b >>= 1;
}
return res;
}
int main(void)
{
freopen("sop.in","r",stdin);
freopen("sop.out","w",stdout);
cin >> n >> k >> m;
for (register int i=1;i<=n;++i) Val[i] = power(i, m);
Dfs(1, 0, 0, 1);
cout << res << endl;
return 0;
}