题解 P4389 付公主的背包

$\texttt{link}$

题意

有 $n$ 种物品,每种物品体积为 $v_i$,且有无限个,求 $\forall i\in \left[1,m\right]$,用这些物品恰好装 $i$ 体积的方案数。

数据范围:$1\le n,m\le10^5,1\le v_i\le m$

题解

这是个 OGF 经典题。

若装 $i$ 体积答案为 $f_i$,写出它的 OGF $F(x)=\sum\limits_{i=0}^\infty f_ix^i$。

若仅有一个体积为 $v$ 的物品,$F(x)=\sum\limits_{i=0}^\infty \left[v|i\right]x^i=\sum\limits_{i\ge 1}x^{iv}$。

如果表为 $1+x^v+x^{2v}+\cdots$,这个等比数列求和很熟悉吧?乘个 $x^v$ 相减得 $F(x)=\sum\limits_{i\ge 1} x^{iv}=\dfrac{1}{1-x^v}$。

那么答案即为 $n$ 个这样的生成函数的卷积。

如果直接暴力乘,复杂度为 $O(nm\log m)$,肯定不行。

前面的形式幂形式不大好搞,我们考虑怎么卷这个封闭形式。

一个经典 trick,可以用取 $\ln$ 的方式把乘变成加,令 $G(x)=\ln F(x)$,有

$$\begin{aligned}G'(x)&=\ln'F(x)\\&=\dfrac{F'(x)}{F(x)}\\&=\dfrac{F'(x)}{\frac{1}{1-x^v}}\\&=(1-x^v)\sum\limits_{i\ge1}iv\cdot x^{iv-1}\\&=\sum\limits_{i\ge1}iv\cdot x^{iv-1}-\sum\limits_{i\ge1}iv\cdot x^{(i+1)v-1}\\&=\sum\limits_{i\ge1}iv\cdot x^{iv-1}-\sum\limits_{i\ge2}(i-1)v\cdot x^{iv-1}\\&=v\cdot x^{v-1}+\sum\limits_{i\ge2}iv\cdot x^{iv-1}-\sum\limits_{i\ge2}(i-1)vx^{iv-1}\\&=\sum\limits_{i\ge1}vx^{iv-1}\end{aligned}$$

两边积回来

$$G(x)=\sum\limits_{i\ge 1}\dfrac{1}{i}x^{iv}$$

于是枚举 $m$ 种体积,如果有该体积的物品直接在对应次项系数上加,最后做一次 $\exp$ 即可。

时间复杂度 $O(m\log m)$。

#include <bits/stdc++.h>
using namespace std;
namespace IO {
    //read and write
} using namespace IO;
const int N = 2.7e5 + 10;
namespace polynomial {
    const int P = 998244353, G = 3, Gi = 332748118;
    int lim, rev[N], a[N], b[N];
    int qpow(int n, int k) {
        int res = 1;
        for(; k; n = 1ll * n * n % P, k >>= 1)
            if(k & 1) res = 1ll * res * n % P;
        return res;
    }
    void NTT(int *f, int T) {
        for(int i = 0; i < lim; i++)
            if(i < rev[i])
                swap(f[i], f[rev[i]]);
        for(int mid = 1; mid < lim; mid <<= 1) {
            int wn = qpow(T == 1 ? G : Gi, (P - 1) / (mid << 1));
            int len = mid << 1;
            for(int i = 0; i < lim; i += (mid << 1)) {
                int w = 1;
                for(int j = 0; j < mid; j++, w = 1ll * w * wn % P) {
                    int x = f[i + j], y = 1ll * w * f[i + j + mid] % P;
                    f[i + j] = (x + y) % P;
                    f[i + j + mid] = (x - y + P) % P;
                }
            }
        }
        if(T == -1) {
            int inv = qpow(lim, P - 2);
            for(int i = 0; i < lim; i++)
                f[i] = 1ll * f[i] * inv % P;
        }
    }
    void init(int n) {
        for(lim = 1; lim < n; lim <<= 1);
        for(int i = 0; i < lim; i++)
            rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (lim >> 1));
    } 
    void mul(int *f, int *g, int *h, int n, int m) {
        static int a[N], b[N];
        init(n + m - 1);
        memset(a, 0, lim << 2);
        memcpy(a, f, n << 2);
        memset(b, 0, lim << 2);
        memcpy(b, g, m << 2);
        NTT(a, 1), NTT(b, 1);
        for(int i = 0; i < lim; i++) 
            h[i] = 1ll * a[i] * b[i] % P;
        NTT(h, -1);
    }
    void inv(int *f, int *g, int n) {
        if(n == 1) { g[0] = qpow(f[0], P - 2); return; }
        inv(f, g, n + 1 >> 1);
        init(n << 1);
        copy(f, f + n, a);
        fill(a + n, a + lim, 0);
        NTT(a, 1), NTT(g, 1);
        for(int i = 0; i < lim; i++)
            g[i] = (2 - 1ll * a[i] * g[i] % P + P) % P * g[i] % P;
        NTT(g, -1);
        fill(g + n, g + lim, 0);
    }
    void dev(int *f, int *g, int n) {
        for(int i = 1; i < n; i++)
            g[i - 1] = 1ll * i * f[i] % P;
        g[n - 1] = 0;
    }
    void invdev(int *f, int *g, int n) {
        for(int i = n - 1; i; i--)
            g[i] = 1ll * f[i - 1] * qpow(i, P - 2) % P;
        g[0] = 0;
    }
    void ln(int *f, int *g, int n) {
        static int a[N];
        init(n << 1);
        memset(a, 0, lim << 2);
        inv(f, a, n);
        dev(f, g, n);
        mul(g, a, g, n, n);
        invdev(g, g, n);
    }
    void exp(int *f, int *g, int n) {
        if(n == 1) { g[0] = 1; return; }
        exp(f, g, n + 1 >> 1);
        ln(g, b, n);
        for(int i = 0; i < n; i++)
            b[i] = (!i + f[i] - b[i] + P) % P;
        fill(b + n, b + lim, 0);
        mul(g, b, g, n, n);
        fill(g + n, g + lim, 0); 
    }
} using namespace polynomial;
int n, m;
int f[N], g[N];
int cnt[N];
int main() {
    m = read(), n = read() + 1;
    for(int i = 1, v; i <= m; i++) {
        v = read();
        cnt[v]++;
    }
    for(int i = 1; i < n; i++) {
        if(!cnt[i]) continue;
        for(int j = i; j < n; j += i)
            f[j] = (f[j] + 1ll * cnt[i] * qpow(j / i, P - 2) % P) % P;
    }
    exp(f, g, n);
    for(int i = 1; i < n; i++)
        printf("%d\n", g[i]);
    return 0;
}
posted @ 2021-12-15 17:01  Terac  阅读(9)  评论(0)    收藏  举报  来源