Luogu4389.付公主的背包

题意:

付公主有一个可爱的背包qwq
这个背包最多可以装 105 10 5 大小的东西
付公主有 n n 种商品,她要准备出摊了.
每种商品体积为Vi,都有 105 10 5 件.
给定m,对于 s[1,m] s ∈ [ 1 , m ] ,请你回答用这些商品恰好装 s s 体积的方案数对998244353取模.

数据范围:

对于30%的数据, n<=3000,m<=3000 n <= 3000 , m <= 3000
对于60%的数据,纯随机生成
对于100%的数据, n<=100000,m<=100000 n <= 100000 , m <= 100000
对于100%的数据, Vi<=m V i <= m

Analysis

首先每种物品都有 105 10 5 个,可以直接看成有无数个物品。考虑生成函数优化:对于任意一个 V V ,设f(x)=i=0xVi=11xV。我们要求的就是所有这样的函数的积。但是乘法不好做,我们考虑将 f(x) f ( x ) ln l n g(x) g ( x ) ,最后 exp e x p 得到答案。

g(x)=f(x)f(x)=(1xV)i=1VixVi1=i=1VxVi1 g ′ ( x ) = f ′ ( x ) f ( x ) = ( 1 − x V ) ∑ i = 1 V ∗ i ∗ x V ∗ i − 1 = ∑ i = 1 V ∗ x V ∗ i − 1

g(x)=i=1VVixVi g ( x ) = ∑ i = 1 V V ∗ i ∗ x V ∗ i

此时我们只需要将所有 g(x) g ( x ) 累加起来,复杂度同筛法 O(mlogm) O ( m l o g m ) ,最后多项式 exp e x p 就好。

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 6e5 + 5;
const int mo = 998244353;
const int g = 3;
const int invg = (mo + 1) / 3;
typedef long long ll;
int A[N],B[N],c[N],d[N],e[N],f[N],w[N][2];
int rev[N],cnt[N],v[N],ans[N],inv[N];
int n,m,l,len;
inline int pow(int x,int p)
{
    int ret = 1;
    for (; p ; p >>= 1,x = (ll)x * x % mo)
        if (p & 1) ret = (ll)ret * x % mo;
    return ret;
}
inline void dft(int *f,int n,int opt)
{
    for (len = 1,l = 0 ; len <= n ; len <<= 1,++l);
    for (int i = 0 ; i < len ; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (l - 1));
    for (int i = 0 ; i < len ; ++i) if (i < rev[i]) swap(f[i],f[rev[i]]);
    for (int i = 1 ; i < len ; i <<= 1)
    {
        int wn = (~opt) ? w[i][0] : w[i][1];
        for (int j = 0 ; j < len ; j += (i << 1))
        {
            int w = 1;
            for (int k = 0 ; k < i ; ++k,w = (ll)w * wn % mo)
            {
                int x = (ll)f[j + k + i] * w % mo,y = f[j + k];
                f[j + k] = (x + y) % mo,f[j + k + i] = (y - x + mo) % mo;
            }
        }
    }
    if (opt == -1)
    {
        int x = pow(len,mo - 2);
        for (int i = 0 ; i < len ; ++i) f[i] = (ll)f[i] * x % mo;
    }
}
inline void getinv(int *a,int *b,int n)
{
    if (n == 1) { b[0] = pow(a[0],mo - 2); return; }
    getinv(a,b,n >> 1);
    for (int i = 0 ; i < n ; ++i) A[i] = a[i],B[i] = b[i];
    dft(A,n << 1,1); dft(B,n << 1,1);
    for (int i = 0 ; i < len ; ++i) A[i] = (ll)A[i] * B[i] % mo * B[i] % mo;
    dft(A,n << 1,-1);
    for (int i = 0 ; i < n ; ++i) b[i] = ((b[i] << 1) % mo - A[i] + mo) % mo;
    for (int i = 0 ; i < len ; ++i) A[i] = B[i] = 0;
}
inline void getln(int *a,int *b,int n)
{
    getinv(a,c,n);
    for (int i = 0 ; i < n - 1 ; ++i) d[i] = (ll)(i + 1) * a[i + 1] % mo;
    dft(c,n << 1,1); dft(d,n << 1,1);
    for (int i = 0 ; i < len ; ++i) c[i] = (ll)c[i] * d[i] % mo;
    dft(c,n << 1,-1);
    for (int i = 1 ; i < n ; ++i) b[i] = (ll)inv[i] * c[i - 1] % mo;
    for (int i = 0 ; i < len ; ++i) c[i] = d[i] = 0;
}
inline void getexp(int *a,int *b,int n)
{
    if (n == 1) { b[0] = 1; return; }
    getexp(a,b,n >> 1);
    for (int i = 0 ; i < n ; ++i) e[i] = b[i];
    getln(b,f,n);
    for (int i = 0 ; i < n ; ++i) f[i] = (mo - f[i] + a[i]) % mo; f[0] = (f[0] + 1) % mo;
    dft(e,n << 1,1); dft(f,n << 1,1);
    for (int i = 0 ; i < len ; ++i) e[i] = (ll)e[i] * f[i] % mo;
    dft(e,n << 1,-1);
    for (int i = 0 ; i < n ; ++i) b[i] = e[i];
    for (int i = 0 ; i < len ; ++i) e[i] = f[i] = 0;
}
int main()
{
    scanf("%d%d",&n,&m); inv[1] = 1;
    for (int i = 1 ; i <= n ; ++i)
    { int x; scanf("%d",&x); ++cnt[x]; }
    int zs = 1; for (; zs <= m ; zs <<= 1);
    for (int i = 1 ; i <= (zs << 1) ; i <<= 1) w[i][0] = pow(g,(mo - 1) / (i << 1)),w[i][1] = pow(invg,(mo - 1) / (i << 1));
    for (int i = 2 ; i < zs ; ++i) inv[i] = (ll)(mo - mo / i) * inv[mo % i] % mo;
    for (int i = 1 ; i <= m ; ++i)
    if (cnt[i])
    {
        int del = (ll)i * cnt[i] % mo;
        for (int j = 1 ; i * j <= m ; ++j) v[j * i] = (v[j * i] + del) % mo;
    }
    for (int i = 1 ; i <= m ; ++i) v[i] = (ll)inv[i] * v[i] % mo;
    getexp(v,ans,zs);
    for (int i = 1 ; i <= m ; ++i) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值