namespace Comb { // 组合数加逆元,依赖于这个MAXN(初始化) vector<ll> frac, infrac; int ln = 1; bool initialized = false; // 在 k=0或者更小的时候 return 1 inline ll binpow(ll a, ll k){ ll res = 1; a %= mod; while (k > 0) { if (k & 1) res = (res * a) % mod; a = (a * a) % mod; k >>= 1; } return res; }
voidComb(int n){ ln = n; frac.assign(n + 5, 1); infrac.assign(n + 5, 1); for (int i = 1; i <= ln; ++i) { frac[i] = (frac[i - 1] * i) % mod; } infrac[ln] = binpow(frac[ln], mod - 2); for (int i = ln - 1; i >= 0; --i) { infrac[i] = (infrac[i + 1] * (i + 1)) % mod; } }
inline ll CC(ll n, ll k){ if (k < 0 || k > n) { #ifdef LOCAL cerr << "CC 组合数传入参数不合法,可能是传入顺序错了\n"; #endif return0; // Return 0 if k is out of range [0, n] } if (!initialized) { Comb(MAXN); initialized = true; } ll res = frac[n]; res = (res * infrac[n - k]) % mod; res = (res * infrac[k]) % mod; return res; }
inline ll PP(ll n, ll k){ if (k < 0 || k > n) { #ifdef LOCAL cerr << "PP 排列数传入参数不合法,可能是传入顺序错了\n"; #endif return0; } if (!initialized) { Comb(MAXN); initialized = true; } ll res = frac[n] * infrac[n - k]; res %= mod; return res; } } // namespace Comb usingnamespace Comb;
voidSolve(){ ll N, M; cin >> N >> M; // i 花色,多取 j 张,内部的数量是多少?(当然,这个多取 j 张,是 A 多取,还是 B 多取,也就是我们一念之间的事情) vector<ll> dp1(M / 2 + 2); for (int j = 0; j <= M / 2; ++j) { dp1[j] = CC(M, M / 2 + j) - CC(M, M / 2 + j + 1); dp1[j] %= mod; } // 在第一个花色多余 j 张的情况下(一开始多取的 ≥ j),处理到 i 花色的时候 // 总共有的这个情况数量 vector<ll> dp2 = dp1; vector<ll> ndp; for (int i = 2; i <= N; ++i) { ndp.assign(M / 2 + 2, 0); for (int j = 0; j <= M / 2; ++j) { for (int k = j; k <= M / 2; ++k) { ndp[j] += dp2[k] * dp1[k - j]; ndp[j] %= mod; } } swap(dp2, ndp); } // 第一个花色,最后必须是剩余 0 才可以 ll ans = dp2[0]; ans %= mod; if (ans < 0) ans += mod; cout << ans << "\n"; }
signedmain(){ ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr); #ifdef LOCAL cout.setf(ios::unitbuf); // 无缓冲流,方便我们调试 #endif