zoukankan      html  css  js  c++  java
  • P4389 付公主的背包

    题意描述

    洛谷·

    付公主有一个可爱的背包qwq。

    这个背包最多可以装 (10^5) 大小的东西

    付公主有 (n) 种商品,她要准备出摊了

    每种商品体积为 (v_i),都有无限件

    给定 (m),对于 (sin [1,m]),请你回答用这些商品恰好装 (s) 体积的方案数。

    数据范围:(n,mleq 10^5,v_ileq 10^5)

    solution

    多项式+生成函数。

    首先一件物品的生成函数显然为:(displaystyle 1+x^{v_i}+x^{2v_i}+x^{3v_i}+cdots) ,写成封闭形式为 (displaystyle 1over 1-x^{v_i})

    恰好装 (s) 体积的方案数的生成函数 (F(x)) ,显然是每个物品的生成函数卷起来之后的结果,即 (displaystyle F(x) = prod_{i=1}^{n} {1over 1-x^{v_i}})

    考虑怎么求 (F(x)), 这玩意暴力展开的会比跑完全背包的复杂度还要差。

    不妨考虑先求出 (ln F(x)), 在 (exp) 回去就可以得到 (F(x))

    显然有:(displaystyle ln F(x) = sum_{i=1}^{n} ln({1over 1-x^{v_i}}))

    那么我们的问题就转化为了怎么求 (displaystyle ln({1over1-x^{v_i}}))

    (G(x) = ln ({1over 1-x^{v_i}})), 对两边同时求导可得:

    (displaystyle G^{prime}(x) = ln^{prime}({1over 1-x^{v_i}}))

    (displaystyle G^{prime}(x) = (1-x^{v_i}){({1over 1-x^{v_i}})^{prime}})

    我们把 ({1over 1-x^{v_i}}) 写成无穷项求和的形式来求导即:

    (displaystyle ({1over 1-x^{v_i}})^{prime} = (sum_{j=0}^{infin} x^{jv_i})^{prime} = sum_{j=0}^{infin} jv_i imes x^{jv_i-1})

    把他带回去可得:

    (displaystyle G^{prime}(x) = left(1-x^{v_i} ight) sum_{j=0}^{infin} jv_i imes x^{jv_i-1})

    (displaystyle G^{prime}(x) = sum_{j=0}^{infin} jv_i imes x^{jv_i-1} - sum_{j=0}^{infin} jv_i imes x^{(j+1)v_i-1})

    因为 (j = 0) 的时候 (jv_i imes x^{jv_i-1} = 0) ,所以我们可以把第一个 (sigma) 中的下标改为从 (1) 开始,至于第二个 (sigma) 我们可以把下标平移,也变为了从 (1) 开始。

    (displaystyle G^prime(x) = sum_{j=1}^{infin} jv_i imes x^{jv_i-1} - sum_{j=1}^{infin} (j-1)v_i imes x^{jv_i-1})

    (displaystyle G^{prime}(x) = sum_{j=1}^{infin} v_i imes x^{jv_i-1})

    (G^{prime}(x)) 积分回去,就可以得到 (G(x)) 即:

    (displaystyle G(x) = int G^{prime}(x)\,dx)

    (displaystyle G(x) = intleft(sum_{j=1}^{infin} v_i imes x^{jv_i-1} ight)\,dx)

    (displaystyle G(x) =sum_{j=1}^{infin} {x^{jv_i}over j})

    则:(displaystyle ln F(x) = sum_{i=1}^{n}sum_{j=0}^{infin} {x^{jv_i}over j})

    对于每个 (v_i), 我们枚举它的倍数,给他倍数位置加上对应的系数,这样的复杂度是调和级数的。

    求出来 (ln F(x)) 之后,我们直接 (exp) 回去即可。

    Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    #define int long long
    const int p = 998244353;
    const int N = 1e6+10;
    int n,m,x,rev[N],a[N],b[N],c[N],A[N],invA[N],invB[N],inv[N],v[N];
    inline int read()
    {
        int s = 0,w = 1; char ch = getchar();
        while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
        while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
        return s * w;
    }
    int ksm(int a,int b)
    {
        int res = 1;
        for(; b; b >>= 1)
        {
            if(b & 1) res = res * a % p;
            a = a * a % p;
        }
        return res;
    }
    void NTT(int *a,int lim,int opt)
    {
        for(int i = 0; i < lim; i++)
        {
            if(i < rev[i]) swap(a[i],a[rev[i]]);
        }
        for(int h = 1; h < lim; h <<= 1)
        {
            int wn = ksm(3,(p-1)/(h<<1));
            if(opt == -1) wn = ksm(wn,p-2);
            for(int j = 0; j < lim; j += (h<<1))
            {
                int w = 1;
                for(int k = 0; k < h; k++)
                {
                    int u = a[j + k];
                    int v = w * a[j + h + k] % p;
                    a[j + k] = (u + v) % p;
                    a[j + h + k] = (u - v + p) % p;
                    w = w * wn % p;
                }
            }
        }
        if(opt == -1)
        {
            int inv = ksm(lim,p-2);
            for(int i = 0; i < lim; i++) a[i] = a[i] * inv % p;
        }
    }
    void Inv(int n,int *a,int *b)
    {
        if(n == 1)
        {
            b[0] = ksm(a[0],p-2);
            return;
        }
        Inv((n+1)>>1,a,b);
        int lim = 1, tim = 0;
        while(lim < (n<<1)) lim <<= 1, tim++;
        for(int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(tim-1));
        for(int i = 0; i < n; i++) c[i] = a[i];
        for(int i = n; i < lim; i++) c[i] = 0;
        NTT(b,lim,1); NTT(c,lim,1);
        for(int i = 0; i < lim; i++) b[i] = (2 * b[i] % p - b[i] * b[i] % p * c[i] % p + p) % p;
        NTT(b,lim,-1);
        for(int i = n; i < lim; i++) b[i] = 0;
    }
    void qiudao(int n,int *a,int *b)
    {
        for(int i = 0; i < n-1; i++) b[i] = a[i+1] * (i+1) % p;
        b[n-1] = 0;
    }
    void jifen(int n,int *a,int *b)
    {
        for(int i = 1; i < n; i++) b[i] = a[i-1] * inv[i] % p;
        b[0] = 0; 
    }
    void Ln(int n,int *a,int *b)
    {
        qiudao(n,a,A); Inv(n,a,invA);
        int lim = 1, tim = 0;
        while(lim < (n<<1)) lim <<= 1, tim++;
        for(int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(tim-1));
        NTT(A,lim,1); NTT(invA,lim,1);
        for(int i = 0; i < lim; i++) A[i] = A[i] * invA[i] % p;
        NTT(A,lim,-1); jifen(n,A,b);
        for(int i = 0; i < lim; i++) A[i] = invA[i] = 0;
    }
    void Exp(int n,int *a,int *b)
    {
        if(n == 1)
        {
            b[0] = 1;
            return;
        }
        Exp((n+1)>>1,a,b);
        Ln(n,b,invB);
        int lim = 1, tim = 0;
        while(lim < (n<<1)) lim <<= 1, tim++;
        for(int i = 0; i < lim; i++) rev[i] = (rev[i>>1]>>1) | ((i&1)<<(tim-1));
        for(int i = 0; i < n; i++) c[i] = a[i];
        for(int i = n; i < lim; i++) c[i] = 0;
        NTT(invB,lim,1); NTT(b,lim,1); NTT(c,lim,1);
        for(int i = 0; i < lim; i++) b[i] = (1 - invB[i] + c[i] + p) % p * b[i] % p;
        NTT(b,lim,-1);
        for(int i = n; i < lim; i++) b[i] = 0;
        for(int i = 0; i < lim; i++) invB[i] = 0;
    }
    signed main()
    {
        n = read(); m = read();
        for(int i = 1; i <= n; i++) v[read()]++;
        for(int i = 1; i <= m; i++) inv[i] = ksm(i,p-2);
        for(int i = 1; i <= m; i++)
        {
            if(!v[i]) continue;
            for(int j = 1; i * j <= m; j++) a[i * j] = (a[i * j] + v[i] * inv[j] % p) % p;
        }
        Exp(m+1,a,b);
        for(int i = 1; i <= m; i++) printf("%lld
    ",b[i]);
        return 0;
    }
    
  • 相关阅读:
    while循环和do while循环的基本使用和区别
    less框架简介
    css关联选择器大致类型总结
    渐进增强和优雅降级
    行,行内元素与块级元素有什么不同?
    for循环的大概遍历运用
    JDBC连接mysql数据库并进行简单操作
    Java实现杨辉三角
    replaceAll() 方法
    java抽象类和接口的区别
  • 原文地址:https://www.cnblogs.com/genshy/p/14535310.html
Copyright © 2011-2022 走看看