清北学堂模拟赛d4t2 b
分析:比较复杂的一题.
首先要求k个mod m互不相同且和为n的数ai,我们可以转化为求和为k个bi,并且(Σbi) % m = n % m
其中bi=ai % m,接下来可以用dp求出选了i个b,和为j的方案数.用f[i][j]表示状态.但是这样可能会让bi重复,一个解决办法是再加上一维,不过这是不必要的,我们只需要先枚举当前的数是哪一个,之后再倒序枚举i,j就可以了.我们知道b的方案数,ai = ki*m + bi,接下来知道ki的方案数就可以了.因为Σai = Σki*m + bi,所有式子全部加起来,就变成了n = (Σki) * m + Σbi,化简一下,可以得到(n - s) / m = Σki,设(n - s) / m = t,接下来的任务就是把t这个数分配给ki,这是隔板法的经典应用,假设有l个k,那么方案数就是C(l + t - 1,t - 1),最后乘上f数组.这样的话求组合数比较麻烦,还要求逆元,注意到我们前面的f[i][j]的求法是假定b1 < b2 < ...... bk的,所以有k!种排列方法,要在答案最后乘上k!,就全部转变为了乘法
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 110, mod = 905229641; const int maxm = (maxn - 1) * maxn / 2; typedef long long ll; ll n, m, f[maxn][maxm], jiecheng[maxn], maxx, ans; ll C(ll a, ll b) { ll res = 1; for (ll i = 1; i < a; i++) res = res * (b + i) % mod; return res; } int main() { scanf("%lld%lld", &n, &m); maxx = (m - 1) * m / 2; f[0][0] = 1; for (int i = 0; i < m; i++) for (int j = m; j >= 0; j--) for (int k = maxx; k >= 0; k--) if (f[j][k]) f[j + 1][k + i] = (f[j + 1][k + i] + f[j][k]) % mod; jiecheng[1] = 1; for (int i = 2; i <= m; i++) jiecheng[i] = (jiecheng[i - 1] * i) % mod; ll minx = n % m; for (ll i = minx; i <= min(n, maxx); i += m) { ll t = (n - i) / m; for (int j = 1; j <= m; j++) if (f[j][i]) { ll temp = C(j, t % mod); temp = (temp * f[j][i]) % mod; temp = (temp * j) % mod; ans = (ans + temp) % mod; } } printf("%lld\n", ans); return 0; }