付公主的背包 题解

付公主的背包 题解

Problem:

​ 有\(n\)种物品,每种物品体积为\(v_i\),数量无限,给定\(m\),求$ \forall s \in [1,m]\(,恰好装\)s$体积的方案数,\(1 \leq n,m \leq 10^5\)

Solution

​ 考虑每一种物品的生成函数\(G_i(x)=\sum\limits_{i=0}^{\infty}x^{iv_i}=\frac{1}{1-x^{v_i}}\)

​ 那么总方案的生成函数就为\(F(x)=\prod\limits_{i=1}^n\frac{1}{1-x^{v_i}}\)

​ 感觉\(\prod\)并不好怎么转化,直接处理肯定过不了,考虑把\(\prod\)化为\(\sum\)

​ 两边取\(ln\),得\(lnF(x)=-\sum\limits_{i=1}^nln(1-x^{v_i})\)

​ 发现现在我们好像仍然没有什么好办法......那么开始推式子都试一遍

​ 考虑等式右边形如\(ln(1-x^k)\)的这个函数,令其为\(H(x)\)

​ 我们有\(H(x)'=-kx^{k-1}\frac{1}{1-x^{k}}\)

​ 幂级数展开:\(H(x)'=-kx^{k-1}\sum\limits_{i=0}^{\infty}x^{ki}=\sum\limits_{i=0}^{\infty}-kx^{ki+k-1}=\sum\limits_{i=1}^{\infty}-kx^{ki-1}\)

​ 逐项积分:\(H(x)=\sum\limits_{i=1}^{\infty}-\frac{x^{ki}}{i}\)

​ ??好像这个式子有点用挺好看的

​ 那么\(lnF(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^{m/v_i}\frac{x^{v_ij}}{j}=\sum\limits_{v=1}^{Max}\sum\limits_{i=1}^{m/v}cnt_v\frac{x^{vi}}{i}\)

​ 算这个式子的复杂度是可以接受的

​ 那么最后再多项式exp回去即可,总复杂度\(O(mlogm)\)

Code

#include<bits/stdc++.h>
#define PR 3
#define mod 998244353
#define LL long long
using namespace std;
int n,m,rev[400005];
LL F[400005],G[400005],H[400005],P[400005],R[400005],inv[400005],bar[400005];

inline char Getchar(){
    static char s[1<<16],*S,*T;
    return (S==T)&&(T=(S=s)+fread(s,1,1<<16,stdin),S==T)?EOF:*S++;
}

inline int read(){
    int x=0,f=1;char ch=Getchar();
    while(ch<'0'||ch>'9'){
       if(ch=='-')f=-1;
       ch=Getchar();
    }
    while(ch>='0'&&ch<='9'){
       x=(x<<1)+(x<<3)+ch-'0';
       ch=Getchar();
    }
    return x*f;
}

inline LL quick_power(LL x,int k){
    LL res=1;
    while(k){
        if(k&1)
            res=res*x%mod;
        x=x*x%mod;k>>=1;
    }
    return res;
}

namespace Polynomial{
    
    void Derivative(LL *F,int Lim){
        for(register int i=0;i<Lim-1;++i)
            F[i]=F[i+1]*(i+1)%mod;
        F[Lim-1]=0;return;
    }

    void Integral(LL *F,int Lim){
        for(register int i=Lim;i;--i)
            F[i]=F[i-1]*inv[i]%mod;
        F[0]=0;return;
    }

    void NTT(LL *F,int Lim,int op){
        for(register int i=0;i<Lim;++i){
            if(i<rev[i])
                swap(F[i],F[rev[i]]);
        }
        for(register int mid=1;mid<Lim;mid<<=1){
            int R=mid<<1;
            LL rt=quick_power(PR,(mod-1)/R);
            for(register int j=0;j<Lim;j+=R){
                LL w=1;
                for(register int k=0;k<mid;++k){
                    LL x=F[j|k],y=w*F[j|k|mid]%mod;
                    F[j|k]=(x+y)%mod;
                    F[j|k|mid]=(x-y+mod)%mod;
                    w=w*rt%mod;
                }
            }
        }
        if(op==-1){
            reverse(F+1,F+Lim);
            LL Inv=quick_power(Lim,mod-2);
            for(register int i=0;i<Lim;++i)
                F[i]=F[i]*Inv%mod;
        }
        return;
    }

    void Polyinv(int N,LL *F,LL *G){
        if(N==1){
            G[0]=quick_power(F[0],mod-2);
            return;
        }
        Polyinv(N+1>>1,F,G);
        int Lim=1,Len=-1;
        while(Lim<(N<<1))   
            Lim<<=1,++Len;
        for(register int i=0;i<Lim;++i)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<Len);
        for(register int i=0;i<Lim;++i)
            H[i]=i<N?F[i]:0;
        NTT(H,Lim,+1);NTT(G,Lim,+1);
        for(register int i=0;i<Lim;++i)
            G[i]=G[i]*(2-G[i]*H[i]%mod+mod)%mod;
        NTT(G,Lim,-1);
        for(register int i=N;i<Lim;++i)
            G[i]=0;
        return;
    }

    void Polyln(int N,LL *F,LL *G){
        Polyinv(N,F,G);
        Derivative(F,N);
        int Lim=1,Len=-1;
        while(Lim<(N<<1))
            Lim<<=1,++Len;
        for(register int i=0;i<Lim;++i)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<Len);
        NTT(F,Lim,+1);NTT(G,Lim,+1);
        for(register int i=0;i<Lim;++i)
            G[i]=F[i]*G[i]%mod;
        NTT(G,Lim,-1);
        Integral(G,N);
        return;
    }

    void Polyexp(int N,LL *F,LL *G){
        if(N==1){
            G[0]=1;
            return;
        }
        Polyexp(N+1>>1,F,G);
        for(register int i=0;i<(N+1>>1);++i)
            R[i]=G[i],P[i]=0;
        Polyln(N,R,P);
        P[0]=F[0]-P[0]+1;
        for(register int i=1;i<(N<<1);++i)
            P[i]=i<N?(F[i]-P[i]+mod)%mod:0;
        int Lim=1,Len=-1;
        while(Lim<(N<<1))
            Lim<<=1,++Len;
        for(register int i=0;i<Lim;++i)
            rev[i]=(rev[i>>1]>>1)|((i&1)<<Len);
        NTT(G,Lim,+1);NTT(P,Lim,+1);
        for(register int i=0;i<Lim;++i)
            G[i]=G[i]*P[i]%mod;
        NTT(G,Lim,-1);
        for(register int i=N;i<Lim;++i)
            G[i]=P[i]=R[i]=0;
        return;
    }

}using namespace Polynomial;

int main(){
    
    n=read();m=read()+1;

    inv[1]=1;
    for(register int i=2;i<=m;++i)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;

    for(register int i=1;i<=n;++i)
        ++bar[read()];

    for(register int i=1;i<=m;++i){
        if(!bar[i])
            continue;
        for(register int j=1;i*j<=m;++j)
            F[i*j]=(F[i*j]+bar[i]*inv[j]%mod)%mod;
    }

    Polyexp(m,F,G);
    
    for(register int i=1;i<m;++i)
        printf("%lld\n",G[i]);

    return 0;
}
posted @ 2020-09-22 20:45  zjy123456  阅读(108)  评论(0编辑  收藏  举报