给定 \(N,M\),求有多少个大小为 \(k\) 的可重集满足所有元素之和为 \(N\),同一个数最多出现 \(M\) 次。对于每个 \(k=1\sim n\) 求出答案。
我们可以将大小为 \(k\) 的可重集转化为长度为 \(k\) 的不降序列,满足第一个数 \(>0\),不存在 \(M\) 个连续且相同的数。
一个不是很常见的套路,对于不降序列,我们作差分,可以将问题转化为任意序列。
我们令 \(B_1 = A_1\),\(B_i=A_{i+1}-A_i\),即 \(B\) 为 \(A\) 的差分数组,那么问题转化为 \(\sum\limits_{i=1}^k(k-i+1)B_i=N\),且不存在连续 \(M\) 个 \(0\)。
后面就很容易了,我们将差分序列倒过来,转化为 \(\sum\limits_{i=1}^kiB_i=N\),不存在连续 \(M\) 个 \(0\),且 \(B_k>0\)。
所以子问题是 \(f_{i,j}\) 表示固定 \(B\) 的前 \(i\) 个数,最后一个数 \(>0\),上面的和为 \(N\) 的方案数。枚举上一个不为 \(0\) 的位置 \(k\),然后枚举 \(B_i\),直接转移的时间复杂度是 \(\mathcal{O}(N^3\ln N)\),前缀和优化后是 \(\mathcal{O}(N^2\ln N)\),实测运行非常快。
#define N 5005
int n, m, f[N][N], s[N][N];
int main() {
n = read(), m = read();
f[0][0] = s[0][0] = 1;
rep(i, 1, n){
rep(j, 1, n){
rep(p, 1, j / i){
ad(f[i][j], s[i - 1][j - p * i]);
if(i > m)su(f[i][j], s[i - m - 1][j - p * i]);
}
s[i][j] = (s[i - 1][j] + f[i][j]) % P;
}
s[i][0] = 1;
}
rp(i, n)printf("%d\n", f[i][n]);
return 0;
}