【2018杭电多校Round8A】Character Encoding(生成函数解法)

题意

m个数字,每个数字0~n-1, 和为k, 有多少种构成方案

分析

问题等效为:将k拆分成m个数字,每个数字0~n-1有多少种方案、

这是一类经典的问题:正整数有序分拆

构造多项式f(x) = 1 + x + x+ x3 +...+xn-1 分别表示该位数字选0、1...n - 1

由乘法原理可知,生成函数为g(x) = (1 + x + x+ 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();
    }
}
View Code

 

posted @ 2018-08-22 02:44  Greenty  阅读(255)  评论(0编辑  收藏  举报