清北学堂模拟赛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
", ans);

    return 0;
}

 

原文地址:https://www.cnblogs.com/zbtrs/p/7648080.html