P4389 付公主的背包 生成函数+多项式ln+多项式exp

P4389 付公主的背包 生成函数

传送门:https://www.luogu.com.cn/problem/P4389

题意

有 n 个 物 品 , 每 个 物 品 体 积 为 v i , 给 定 一 个 m , 问 这 些 物 品 恰 好 装 [ 1 , m ] 的 方 案 数 。 有n个物品,每个物品体积为v_i,给定一个m,问这些物品恰好装[1,m]的方案数。 nvim[1,m]

思路

如 果 数 据 量 小 , 则 可 以 背 包 。 如果数据量小,则可以背包。
但 问 题 是 , n 和 m 都 有 1 e 5 , 不 可 能 背 包 的 。 但问题是,n和m都有1e5,不可能背包的。 nm1e5

因 为 是 一 类 计 数 问 题 , 所 以 考 虑 生 成 函 数 。 因为是一类计数问题,所以考虑生成函数。

设 只 装 一 个 物 品 体 积 v , 它 可 以 用 多 大 的 背 包 , 那 就 是 v , 2 v , . . n v 。 设只装一个物品体积v,它可以用多大的背包,那就是v,2v,..nv。 vv2v..nv

设 生 成 函 数 为 F ( x ) = ∑ i = 0 ∞ x i v 设生成函数为F(x)=\sum_{i=0}^{\infty }x^{iv} F(x)=i=0xiv

则 , 对 于 一 个 s 背 包 , a n s = 所 有 生 成 函 数 卷 积 , 则 复 杂 度 为 O ( n log ⁡ n ) 。 则,对于一个s背包,ans=所有生成函数卷积,则复杂度为O(n\log n)。 sans=O(nlogn)
对 于 [ 1 , m ] 所 有 背 包 , 复 杂 度 为 O ( m n log ⁡ n ) , 也 很 爆 炸 对于[1,m]所有背包,复杂度为O(mn\log n),也很爆炸 [1,m]O(mnlogn)

所 以 考 虑 生 成 函 数 的 封 闭 形 式 来 简 化 问 题 。 所以考虑生成函数的封闭形式来简化问题。

F ( x ) = ∑ i = 0 ∞ x i v = 1 1 − x v F(x)=\sum_{i=0}^{\infty }x^{iv}=\frac{1}{1-x^{v}} F(x)=i=0xiv=1xv1

那 我 们 可 以 把 封 闭 形 式 的 求 卷 积 , 再 来 一 个 多 项 式 求 逆 。 那我们可以把封闭形式的求卷积,再来一个多项式求逆。
复 杂 度 为 O ( 2 m ) , 还 是 爆 炸 , 那 怎 么 办 呢 ? 复杂度为O(2^m),还是爆炸,那怎么办呢? O(2m)

上 面 两 种 方 法 , 都 是 因 为 卷 积 , 如 果 我 们 能 不 乘 法 , 而 改 为 加 法 , 那 复 杂 度 是 不 是 可 以 了 ? 上面两种方法,都是因为卷积,如果我们能不乘法,而改为加法,那复杂度是不是可以了?

乘 法 变 加 法 , 取 对 数 , 多 项 式 取 ln ⁡ 的 复 杂 度 也 是 O ( n log ⁡ n ) , 那 对 所 有 的 F ( x ) 取 , 不 是 也 会 T 吗 ? 乘法变加法,取对数,多项式取\ln的复杂度也是O(n\log n),那对所有的F(x)取,不是也会T吗? lnO(nlogn)F(x)T

先 来 看 看 这 个 多 项 式 取 ln ⁡ 是 什 么 形 式 。 先来看看这个多项式取\ln 是什么形式。 ln

设 G ( x ) = ln ⁡ F ( x ) , 则 设G(x)=\ln F(x),则 G(x)=lnF(x)

G ′ ( x ) = F ′ ( x ) F ( x ) G'(x)=\frac{F'(x)}{F(x)} G(x)=F(x)F(x)

G ′ ( x ) = − − v ∗ x v − 1 1 − x v G'(x)=-\frac{-v*x^{v-1}}{1-x^{v}} G(x)=1xvvxv1

G ′ ( x ) = v ∗ x v − 1 1 − x v G'(x)=\frac{v*x^{v-1}}{1-x^v} G(x)=1xvvxv1

G ′ ( x ) = v x v − 1 ∗ ∑ i = 0 ∞ x i v G'(x)=vx^{v-1}*\sum_{i=0}^{\infty} x^{iv} G(x)=vxv1i=0xiv

G ′ ( x ) = v ∑ i = 0 ∞ x i v + v − 1 G'(x)=v\sum_{i=0}^{\infty} x^{iv+v-1} G(x)=vi=0xiv+v1

G ( x ) = v ∑ i = 0 ∞ x i v + v i v + v G(x)=v\sum_{i=0}^{\infty} \frac{x^{iv+v}}{iv+v} G(x)=vi=0iv+vxiv+v

G ( x ) = ∑ i = 0 ∞ x v ( i + 1 ) i + 1 G(x)=\sum_{i=0}^{\infty}\frac{x^{v(i+1)}}{i+1} G(x)=i=0i+1xv(i+1)

G ( x ) = ∑ i = 1 ∞ x i v i G(x)=\sum_{i=1}^{\infty}\frac{x^{iv}}{i} G(x)=i=1ixiv

所 以 我 们 可 以 先 对 每 个 物 品 的 体 积 计 数 , 然 后 分 配 给 对 应 的 x i 。 所以我们可以先对每个物品的体积计数,然后分配给对应的x^i。 xi

最 后 多 项 式 e x p 得 到 原 来 多 项 式 , 并 输 出 。 最后多项式exp得到原来多项式,并输出。 exp

Code

#include "bits/stdc++.h"
using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<double, double> pdd;
typedef pair<ll, ll> pll;

#define endl "\n"
#define eb emplace_back
#define mem(a, b) memset(a , b , sizeof(a))

const ll INF = 0x3f3f3f3f;
const ll mod = 998244353;
// const ll mod = 1e9 + 7;
const double eps = 1e-6;
const double PI = acos(-1);
const double R = 0.57721566490153286060651209;

const int N = 4e5 + 10;

struct Complex {
    double x, y;
    Complex(double a = 0, double b = 0): x(a), y(b) {}
    Complex operator + (const Complex &rhs) { return Complex(x + rhs.x, y + rhs.y); }
    Complex operator - (const Complex &rhs) { return Complex(x - rhs.x, y - rhs.y); }
    Complex operator * (const Complex &rhs) { return Complex(x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x); }
    Complex conj() { return Complex(x, -y); }
} w[N];

int tr[N];
ll EXP[N], V[N], ans[N];

ll quick_pow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans % mod;
}

int getLen(int n) {
    int len = 1; while (len < (n << 1)) len <<= 1;
    for (int i = 0; i < len; i++) tr[i] = (tr[i >> 1] >> 1) | (i & 1 ? len >> 1 : 0);
    for (int i = 0; i < len; i++) w[i] = w[i] = Complex(cos(2 * PI * i / len), sin(2 * PI * i / len));
    return len;
}

void FFT(Complex *A, int len) {
    for (int i = 0; i < len; i++) if(i < tr[i]) swap(A[i], A[tr[i]]);
    for (int i = 2, lyc = len >> 1; i <= len; i <<= 1, lyc >>= 1)
        for (int j = 0; j < len; j += i) {
            Complex *l = A + j, *r = A + j + (i >> 1), *p = w;
            for (int k = 0; k < i >> 1; k++) {
                Complex tmp = *r * *p;
                *r = *l - tmp, *l = *l + tmp;
                ++l, ++r, p += lyc;
            }
        }
}

inline void MTT(ll *x, ll *y, ll *z, int len) {

    for (int i = 0; i < len; i++) (x[i] += mod) %= mod, (y[i] += mod) %= mod;
    static Complex a[N], b[N];
    static Complex dfta[N], dftb[N], dftc[N], dftd[N];

    for (int i = 0; i < len; i++) a[i] = Complex(x[i] & 32767, x[i] >> 15);
    for (int i = 0; i < len; i++) b[i] = Complex(y[i] & 32767, y[i] >> 15);
    FFT(a, len), FFT(b, len);
    for (int i = 0; i < len; i++) {
        int j = (len - i) & (len - 1);
        static Complex da, db, dc, dd;
        da = (a[i] + a[j].conj()) * Complex(0.5, 0);
        db = (a[i] - a[j].conj()) * Complex(0, -0.5);
        dc = (b[i] + b[j].conj()) * Complex(0.5, 0);
        dd = (b[i] - b[j].conj()) * Complex(0, -0.5);
        dfta[j] = da * dc;
        dftb[j] = da * dd;
        dftc[j] = db * dc;
        dftd[j] = db * dd;
    }
    for (int i = 0; i < len; i++) a[i] = dfta[i] + dftb[i] * Complex(0, 1);
    for (int i = 0; i < len; i++) b[i] = dftc[i] + dftd[i] * Complex(0, 1);
    FFT(a, len), FFT(b, len);
    for (int i = 0; i < len; i++) {
        ll da = (ll)(a[i].x / len + 0.5) % mod;
        ll db = (ll)(a[i].y / len + 0.5) % mod;
        ll dc = (ll)(b[i].x / len + 0.5) % mod;
        ll dd = (ll)(b[i].y / len + 0.5) % mod;
        z[i] = (da + ((ll)(db + dc) << 15) + ((ll)dd << 30)) % mod;
    }
}

void Get_Inv(ll *f, ll *g, int n) {
    if(n == 1) { g[0] = quick_pow(f[0], mod - 2); return ; }
    Get_Inv(f, g, (n + 1) >> 1);

    int len = getLen(n);
    static ll c[N];
    for(int i = 0;i < len; i++) c[i] = i < n ? f[i] : 0;
    MTT(c, g, c, len); MTT(c, g, c, len);
    for(int i = 0;i < n; i++) g[i] = (2ll * g[i] - c[i] + mod) % mod;
    for(int i = n;i < len; i++) g[i] = 0;
    for(int i = 0;i < len; i++) c[i] = 0;
}

void Get_Der(ll *f, ll *g, int len) { for(int i = 1;i < len; i++) g[i - 1] = f[i] * i % mod; g[len - 1] = 0; }

void Get_Int(ll *f, ll *g, int len) { for(int i = 1;i < len; i++) g[i] = f[i - 1] * quick_pow(i, mod - 2) % mod; g[0] = 0; }

void Get_Ln(ll *f, ll *g, int n) {
    static ll a[N], b[N];
    Get_Der(f, a, n);
    Get_Inv(f, b, n);
    int len = getLen(n);
    MTT(a, b, a, len);
    Get_Int(a, g, len);
    for(int i = n;i < len; i++) g[i] = 0;
    for(int i = 0;i < len; i++) a[i] = b[i] = 0;
}

void Get_Exp(ll *f, ll *g, int n) {
    if(n == 1) return (void)(g[0] = 1);
    Get_Exp(f, g, (n + 1) >> 1);

    static ll a[N];
    Get_Ln(g, a, n);
    a[0] = (f[0] + 1 - a[0] + mod) % mod;
    for(int i = 1;i < n; i++) a[i] = (f[i] - a[i] + mod) % mod;
    int len = getLen(n);
    MTT(a, g, g, len);
    for(int i = n;i < len; i++) g[i] = 0;
    for(int i = 0;i < len; i++) a[i] = 0;
}

void solve() {
    int n, m; cin >> n >> m;
    for(int i = 1;i <= n; i++) {
        int v; cin >> v;
        V[v]++;
    }

    for(int v = 1;v <= m; v++) {
        if(V[v]) {
            for(int i = 1;i <= m / v; i++) {
                EXP[i * v] = (EXP[i * v] + 1ll * V[v] * quick_pow(i, mod - 2)) % mod;
            }
        }
    }

    Get_Exp(EXP, ans, m + 1);

    for(int i = 1;i <= m; i++) cout << ans[i] << endl;

}

signed main() {
    ios_base::sync_with_stdio(false);
    // cin.tie(nullptr);
    // cout.tie(nullptr);
#ifdef FZT_ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    signed test_index_for_debug = 1;
    char acm_local_for_debug = 0;
    do {
        if (acm_local_for_debug == '$') exit(0);
        if (test_index_for_debug > 20)
            throw runtime_error("Check the stdin!!!");
        auto start_clock_for_debug = clock();
        solve();
        auto end_clock_for_debug = clock();
        cout << "Test " << test_index_for_debug << " successful" << endl;
        cerr << "Test " << test_index_for_debug++ << " Run Time: "
             << double(end_clock_for_debug - start_clock_for_debug) / CLOCKS_PER_SEC << "s" << endl;
        cout << "--------------------------------------------------" << endl;
    } while (cin >> acm_local_for_debug && cin.putback(acm_local_for_debug));
#else
    solve();
#endif
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值