1. 程式人生 > >清北學堂模擬賽d4t2 b

清北學堂模擬賽d4t2 b

ace 設有 得到 轉變 模擬 個數 ima mes ++

技術分享

分析:比較復雜的一題.

首先要求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; }

清北學堂模擬賽d4t2 b