【2018杭电多校Round8A】Character Encoding(生成函数解法)
题意
m个数字,每个数字0~n-1, 和为k, 有多少种构成方案
分析
问题等效为:将k拆分成m个数字,每个数字0~n-1有多少种方案、
这是一类经典的问题:正整数有序分拆
构造多项式f(x) = 1 + x + x2 + x3 +...+xn-1 分别表示该位数字选0、1...n - 1
由乘法原理可知,生成函数为g(x) = (1 + x + x2 + x3 +...+xn-1)m = (1-xn)m/(1-x)m
推导 g(x) = (1-xn)m * (1-x)-m
广义二项式定理可以展开-m次方
推导 g(x) = (∑(-1)^i * C(m,i) * xn-i ) * (Σ C(m-1+i,m-1) * xi )
xk中的k可以由上式前半部分的0n,1n,2n ... 分别和后半部分的k-0n,k-1n,...相乘得到
所以xk的系数为∑(-1)-1 * C(m,i)*C(m-1+k-n,m-1)
预处理阶乘逆元就能在o(1)求出组合数
代码
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+2005, MOD = 998244353; ll fac[N], facinv[N]; ll n, m, k; ll qpow(ll a, ll b) { ll ans = 1; while(b) { if(b & 1) ans = ans * a % MOD; a = a * a % MOD; b >>= 1; } return ans; } ll C(ll a, ll b) { ll res = (fac[a] * facinv[a - b] % MOD)* facinv[b] % MOD; return res; } void ycl() { fac[0] = 1; for(int i = 1; i < N; i++) fac[i] = fac[i-1] * i % MOD; facinv[N-1] = qpow(fac[N-1], MOD-2); for(int i =N-2; i>=0; i--) facinv[i] = facinv[i+1] * (i+1) % MOD; } void solve() { int o = 1; ll ans = 0; for(int i = 0; i * n <= k && i <= m; i++) { ans += o * C(m, i) * C(m - 1 + k - n * i, m - 1); ans = (ans % MOD + MOD) % MOD; // cout<<ans<<endl; o = -o; } printf("%lld\n", ans); } int main() { ycl(); int _;scanf("%d",&_); while(_--) { scanf("%lld%lld%lld", &n, &m, &k); solve(); } }