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

    luogu4389:付公主的背包

    题意:

    (nleq 10^5)个东西,每个物品有体积(v_i),有无限件。

    给定(m),对于(sin [1,m]),问用这些物品恰好装(s)体积的方案数。

    (998244353)取模。

    思路:

    如果数据是(10^4),那么这就是一道完全背包的题目。

    但显然此时我们需要效率更高的算法。

    我们知道在求凑成某种数值的方案数的时候可以先设计母函数。

    [G(x)=prod(1+x^{v_i}+x^{2v_i}+...) ]

    这个公式展开后,对于(sin[1,m])的项数的系数就是对应方案数。

    暴力展开的话,时间复杂度会达到(O(n^2logn)),还不如写完全背包。

    考虑优化。

    (i)个物品的多项式为:

    [f(i)=sum_{j=0}x^{jv_i}=frac{1}{1-x^{v_i}} ]

    可能有学过收敛域的同学可能会觉得有些疑惑,(p)级数要求公比的绝对值严格小于(1),这里其实(x)只是一个符号而已,取值自定,所以我可以假设他是小于(1)的公比。

    所以我们需要的多项式就是:

    [g=prod_{i=1}^nf(i) ]

    改乘法为加法:

    [ln(g)=prod ln(f(i))=ln(sum f(i)) ]

    那么多项式如何求(ln)呢?

    [lnf=int frac{f'}{f} ]

    我们把(f(i))带入,下面用(v)代替(v_i)

    [lnf(i)=lnfrac{1}{1-x^v}=int frac{vx^{v-1}}{1-x^v}dx ]

    (f(i)=frac{1}{1-x^v})代入:

    [int vx^{v-1}f(i)=intsum_{i=0}^infin x^{iv} imes vx^{v-1}=intsum_{i=1}^infin vx^{iv-1} ]

    积分:

    [sum_{i=1}^infin frac{1}{i}x^{iv} ]

    这也就是(ln(sum f(i)))了。

    但是我们要求的是(prod f(i)),所以还需要(exp)回来。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int G = 3;
    const int mod = 998244353;
    const int maxn = 5e5+10;
    
    ll qmi(ll a, ll b)
    {
        ll res = 1;
        while(b)
        {
            if(b&1) res = (res*a)%mod;
            b >>= 1;
            a = (a*a)%mod;
        } return res%mod;
    }
    
    int limit, len, rev[maxn];
    void NTT(ll *c, int op)
    {
        for(int i = 1; i <= limit; i++)
            if(i < rev[i]) swap(c[i], c[rev[i]]);
    
        for(int mid = 1; mid < limit; mid <<= 1)
        {
            ll gn = qmi(G, (mod-1)/(mid<<1));
            if(op == -1) gn = qmi(gn, mod-2);
            for(int j = 0, R = mid<<1; j < limit; j += R)
            {
                ll g = 1;
                for(int k = 0; k < mid; k++, g = (g*gn)%mod)
                {
                    ll x = c[j+k], y = g*c[j+k+mid]%mod;
                    c[j+k] = (x+y)%mod;
                    c[j+k+mid] = (x-y+mod)%mod;
                }
            }
        }
        if(op == 1) return;
        ll inv = qmi(limit, mod-2);
        for(int i = 0; i <= limit; i++) c[i] = c[i]*inv%mod;
    }
    
    ll tmp[maxn];
    void get_inv(ll *a, ll *b, int deg)
    {
    	if(deg == 1)
    	{
    		b[0] = qmi(a[0], mod-2);
    		return;
    	}
    	get_inv(a, b, (deg+1)>>1);
    
    	len = 0, limit = 1;
    	while(limit <= (deg+deg)) limit <<= 1, len++;
    
    	for(int i = 1; i <= limit; i++)
    		rev[i] = (rev[i>>1]>>1)|((i&1)<<(len-1));
    
    	for(int i = 0; i < deg; i++) tmp[i] = a[i];
    	for(int i = deg; i <= limit; i++) tmp[i] = 0;
    
    	NTT(tmp, 1), NTT(b, 1);
    	for(int i = 0; i <= limit; i++)
    		b[i] = 1ll*(2-1ll*tmp[i]*b[i]%mod+mod)%mod*b[i]%mod;
    	NTT(b, -1);
    	for(int i = deg; i <= limit; i++) b[i] = 0;
    }
    
    void deriv(ll *a, int n)
    {
        for(int i = 1; i < n; i++)
            a[i-1] = i*a[i]%mod;
        a[n-1] = 0;
    }
    
    void integ(ll *a, int n)
    {
        for(int i = n-1; i > 0; i--)
            a[i] = a[i-1]*qmi(i, mod-2)%mod;
        a[0] = 0;
    }
    
    void get_ln(ll *a, ll *b, int deg)
    {
        get_inv(a, b, deg);
        for(int i = 0; i < deg; i++)
            tmp[i] = a[i];
        for(int i = deg; i <= limit; i++)
            tmp[i] = 0;
        deriv(tmp, deg);
        NTT(tmp, 1); NTT(b, 1);
        for(int i = 0; i <= limit; i++)
            b[i] = b[i]*tmp[i]%mod;
        NTT(b, -1);
        integ(b, deg);
    }
    
    ll c[maxn];
    void get_exp(ll *a, ll *b, int n)
    {
        if(n == 1) {
            b[0] = 1;
            return;
        }
        get_exp(a, b, (n+1)>>1);
        memset(c, 0, sizeof c);
        get_ln(b, c, n); // c = lnb
        c[0] = (1-c[0]+a[0]+mod)%mod;
        for(int i = 1; i < n; i++)
            c[i] = (a[i]-c[i]+mod)%mod;
        NTT(c, 1); NTT(b, 1);
        for(int i = 0; i <= limit; i++)
            b[i] = b[i]*c[i]%mod;
        NTT(b, -1);
        for(int i = n; i <= limit; i++)
            b[i] = 0;
    }
    
    int n, m;
    int cnt[maxn];
    ll f[maxn], g[maxn];
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i = 1, x; i <= n; i++){
            scanf("%d", &x);
            cnt[x]++;
        }
    
        for(int v = 1; v <= m; v++)
        if(cnt[v])
        {
            for(int iv= v; iv <= m; iv += v)
            f[iv]=(f[iv]+cnt[v]*qmi(iv/v,mod-2)%mod)%mod;
        }
    
        get_exp(f, g, m+1);
        for(int i = 1; i <= m; i++)
            printf("%lld
    ", g[i]);
        return 0;
    }
    
    
  • 相关阅读:
    Balance的数学思想构造辅助函数
    1663. Smallest String With A Given Numeric Value (M)
    1680. Concatenation of Consecutive Binary Numbers (M)
    1631. Path With Minimum Effort (M)
    1437. Check If All 1's Are at Least Length K Places Away (E)
    1329. Sort the Matrix Diagonally (M)
    1657. Determine if Two Strings Are Close (M)
    1673. Find the Most Competitive Subsequence (M)
    1641. Count Sorted Vowel Strings (M)
    1679. Max Number of K-Sum Pairs (M)
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/12490277.html
Copyright © 2011-2022 走看看