\(n\)个人,编号\(i\sim n\),分成\(k\)组(不一定要连续分组),使任意一组内不存在两个人的编号模\(m\)同余,问方案,取模.
输入\(n,m\),输出\(k\)取\(1\sim n\)的所有答案.
思路这题不比F水?
设\(f_{i,j}\)表示前\(j\)个人,分成\(i\)组的合法方案数,目标显然为\(f_{i,n},\quad i\in[1,n]\).
考虑从前\(j-1\)个人转移,对于第\(j\)个人,有两种方案:
- 单独开一个组,贡献是\(f_{i-1,j-1}\)
- 放到前面的某一个组,注意,前面有\(i\)个组,但是有\(\lfloor \frac{j-1}m\rfloor\)个组中存在和\(j\)模\(m\)同余的数,所以贡献是\(f_{i,j-1}\cdot (i-\lfloor \frac{j-1}m\rfloor)\).
转移是\(O(1)\)的,复杂度就是\(O(n^2)\).
代码#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const LL mod = 998244353;
const int N = 5010;
int n , m;
LL f[N][N];
int main() {
cin >> n >> m;
for(int i = 1 ; i <= m ; i++)
f[1][i] = 1;
for(int i = 2 ; i <= n ; i++)//i groups
for(int j = 1 ; j <= n ; j++) {//first j people
f[i][j] = f[i - 1][j - 1] + f[i][j - 1] * (i - (j - 1) / m) % mod;
f[i][j] %= mod;
}
for(int i = 1 ; i <= n ; i++) {
printf("%d\n" , f[i][n]);
}
return 0;
}