[2023杭电多校5 1005] Snake (生成函数)

题意

n n n 个标号为 1 , 2 , ⋯   , n 1,2,\cdots,n 1,2,,n 的球,放到 m m m 个无标号盒子 (盒内顺序有标号),且每个盒子球数不超过 k k k,求方案数对 998   244   353 998\,244\,353 998244353 取模。

1 ≤ m , k ≤ n ≤ 1 0 6 1 \le m,k \le n \le 10 ^ 6 1m,kn106

分析:

考虑每个盒子内球的生成函数 ∑ i = 1 k x i \sum\limits_{i = 1} ^ {k}x ^ i i=1kxi,那么 m m m 个盒子的生成函数就为 ( ∑ i = 1 k x i ) m \left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m (i=1kxi)m,那么方案数就为第 n n n 项系数

由于球带标号,所以需要对答案全排列,也就是乘 n ! n! n!,又由于盒子不带标号,所以要对答案除 m ! m! m!,那么答案为

n ! m ! × [ x n ] ( ∑ i = 1 k x i ) m \frac{n!}{m!} \times [x ^ n]\left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m m!n!×[xn](i=1kxi)m

1 0 6 10 ^ 6 106 用多项式快速幂会超时,考虑

( ∑ i = 1 k x i ) m = x m ( ∑ i = 0 k − 1 x i ) m = x m ( 1 − x k ) m ( 1 − x ) m \left( \sum\limits_{i = 1} ^ {k}x ^ i\right) ^ m= x ^ m \left( \sum\limits_{i = 0} ^ {k - 1}x ^ i\right) ^ m = x ^ m \frac{(1 -x ^ k)^m}{(1 - x) ^ m} (i=1kxi)m=xm(i=0k1xi)m=xm(1x)m(1xk)m

转为求 [ x n − m ] ( 1 − x k ) m ( 1 − x ) m [x^{n - m}] \dfrac{(1 -x ^ k)^m}{(1 - x) ^ m} [xnm](1x)m(1xk)m 其中

( 1 − x k ) m = ∑ i = 0 m ( m i ) × ( − 1 ) i × x i × k 1 ( 1 − x ) m = ∑ i = 0 ∞ ( m − 1 + i m − 1 ) × x i (1 - x ^ k) ^ m = \sum_{i = 0} ^ {m}\binom{m}{i} \times (-1) ^ i \times x ^ {i \times k} \\ \frac{1}{(1 - x) ^ m} = \sum_{i = 0} ^ {\infty} \binom{m - 1 + i}{m - 1} \times x ^ i (1xk)m=i=0m(im)×(1)i×xi×k(1x)m1=i=0(m1m1+i)×xi

于是枚举第一个式子的 i i i,那么只需要求第二个式子的 n − m − i × k n - m - i \times k nmi×k 项系数即可。预处理组合数即可。

代码:

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
constexpr int mod = 998244353;
template<class T>
T power(T a, int b) {
    T res = 1;
    for (; b; b /= 2, a *= a) {
        if (b % 2) {
            res *= a;
        }
    }
    return res;
}
template<int mod>
struct ModInt {
    int x;
    ModInt() : x(0) {}
    ModInt(i64 y) : x(y >= 0 ? y % mod : (mod - (-y) % mod) % mod) {}
    ModInt &operator+=(const ModInt &p) {
        if ((x += p.x) >= mod) x -= mod;
        return *this;
    }
    ModInt &operator-=(const ModInt &p) {
        if ((x += mod - p.x) >= mod) x -= mod;
        return *this;
    }
    ModInt &operator*=(const ModInt &p) {
        x = (int)(1LL * x * p.x % mod);
        return *this;
    }
    ModInt &operator/=(const ModInt &p) {
        *this *= p.inv();
        return *this;
    }
    ModInt operator-() const {
        return ModInt(-x);
    }
    ModInt operator+(const ModInt &p) const {
        return ModInt(*this) += p;
    }
    ModInt operator-(const ModInt &p) const {
        return ModInt(*this) -= p;
    }
    ModInt operator*(const ModInt &p) const {
        return ModInt(*this) *= p;
    }
    ModInt operator/(const ModInt &p) const {
        return ModInt(*this) /= p;
    }
    bool operator==(const ModInt &p) const {
        return x == p.x;
    }
    bool operator!=(const ModInt &p) const {
        return x != p.x;
    }
    ModInt inv() const {
        int a = x, b = mod, u = 1, v = 0, t;
        while (b > 0) {
            t = a / b;
            swap(a -= t * b, b);
            swap(u -= t * v, v);
        }
        return ModInt(u);
    }
    ModInt pow(i64 n) const {
        ModInt res(1), mul(x);
        while (n > 0) {
            if (n & 1) res *= mul;
            mul *= mul;
            n >>= 1;
        }
        return res;
    }
    friend ostream &operator<<(ostream &os, const ModInt &p) {
        return os << p.x;
    }
    friend istream &operator>>(istream &is, ModInt &a) {
        i64 t;
        is >> t;
        a = ModInt<mod>(t);
        return (is);
    }
    int val() const {
        return x;
    }
    static constexpr int val_mod() {
        return mod;
    }
};
using Z = ModInt<mod>;
vector<Z> fact, infact;
void init(int n) {
    fact.resize(n + 1), infact.resize(n + 1);
    fact[0] = infact[0] = 1;
    for (int i = 1; i <= n; i ++) {
        fact[i] = fact[i - 1] * i;
    }
    infact[n] = fact[n].inv();
    for (int i = n; i; i --) {
        infact[i - 1] = infact[i] * i;
    }
}
Z C(int n, int m) {
    if (n < 0 || m < 0 || n < m) return Z(0);
    return fact[n] * infact[n - m] * infact[m];
}
void solve() {
    int n, m, k;
    cin >> n >> m >> k;
    Z ans;
    for (int i = 0; i <= m; i ++) {
        Z f = i & 1 ? Z(-1) : Z(1);
        ans += f * C(m, i) * C(n - k * i - 1, m - 1);
    }
    cout << ans * fact[n] / fact[m] << "\n";
}
signed main() {
    init(1e6);
    cin.tie(0) -> sync_with_stdio(0);
    int T;
    cin >> T;
    while (T --) {
        solve();
    }
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值