zoukankan      html  css  js  c++  java
  • P4389 付公主的背包 生成函数

    题意:

    戳这里

    分析:

    • 暴力

    直接完全背包,复杂度 (O(nmfrac{m}{v}))

    • 优化

    (O(frac{nm}{v}))写出生成函数 (O(n^2log)) 乘起来,总复杂度 (O(frac{nm}{v}+n^2log ))

    • 正解

    我们发现这些乘法操作太多了,所以按照套路对于 (n) 个式子取一遍离散对数,然后加起来再 exp 回去

    那么我们开始推柿子了,首先原式 (displaystyle f(n)=sum_{i=0}^{frac{m}{v}}x^{iv}) , 我们要求它的对数

    [f(n)=frac{1}{1-x^v} \ ln(f(n))=ln(frac{1}{1-x^v})=ln(1)-ln(1-x^v)=-ln(1-x^v) ]

    我们把要求的东西转化了一下,记 (F(x)=ln(1-x^v),G(x)=ln(F(x))) ,先求导

    [G'(x)=ln'(F(x))F'(x) \ =frac{F'(x)}{F(x)} \ =frac{-vx^{v-1}}{1-x^v} \ =-vx^{v-1}frac{1}{1-x^v} \ =-vx^{v-1}sum_{i=0}^{infty}(x^v)^i \ =sum_{i=0}^{infty}-vx^{iv+v-1} \ =sum_{i=0}^{infty}-vx^{iv-1} ]

    然后再积分回去得到

    [G(x)=int G'(x) \ =int sum_{i=0}^{infty}-vx^{iv-1} \ =sum_{i=0}^{infty} int -vx^{iv-1} \ =sum_{i=0}^{infty} frac{-v}{iv}x^{iv} \ =sum_{i=0}^{infty} -frac{1}{i}x^{iv} ]

    至此我们得到了价值为 (v) 的物品的生成函数的离散对数,处理一个物品的复杂度还是 (O(frac{m}{v})) ,不够优秀,我们发现价值相同的物品的生成函数长的一模一样,所以我们考虑开一个桶,存一下每种价值的物品个数,然后每一种价值还要乘上 (cnt[i]) (相当于 (cnt[i]) 个这样的离散对数相加) ,这样处理的复杂度是调和级数 (O(mlog))

    整体复杂度变成了 (O(mlog))

    代码:

    #include<bits/stdc++.h>
    #define inl inline
    #define reg register
    
    using namespace std;
    
    namespace zzc
    {
        typedef long long ll;
        inl ll read()
        {
            ll x=0,f=1;char ch=getchar();
            while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
            while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
            return x*f;
        }
    
        const int maxn = 3e5+5;
        const ll mod = 998244353;
        ll n,lim,l,m;
        ll v[maxn],cnt[maxn];
        ll a[maxn],b[maxn],c[maxn],d[maxn],e[maxn],f[maxn],g[maxn],rev[maxn];
    
       	ll qpow(ll x,ll y)
    	{
    		ll res=1;
    		while(y)
    		{
    			if(y&1) res=res*x%mod;
    			x=x*x%mod;
    			y>>=1;
    		}
    		return res;
    	}
    	
    	void ntt(ll *a,ll inv)
    	{
    		for(ll i=0;i<lim;i++) if(i<rev[i]) swap(a[i],a[rev[i]]);
    		for(ll len=1;len<lim;len<<=1)
    		{
    			ll w1=qpow(inv==1?3:332748118,(mod-1)/(len<<1));
    			for(ll i=0;i<lim;i+=(len<<1))
    			{
    				ll w=1;
    				for(ll j=0;j<len;j++)
    				{
    					ll x=a[i+j],y=w*a[i+j+len]%mod;
    					a[i+j]=(x+y)%mod;
    					a[i+j+len]=(x-y+mod)%mod;
    					w=w*w1%mod;
    				}
    			}
    		}
    		if(inv==1) return ;
    		long long tmp=qpow(lim,mod-2);
    		for(int i=0;i<=lim;i++) a[i]=(a[i]*tmp%mod+mod)%mod;
    	}
    	
    	void get_len(int k)
    	{
    		lim=1,l=0;
    		while(lim<(k<<1)) lim<<=1,l++;
    		for(ll i=0;i<lim;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1));
    	}
    	
    	void get_inv(ll *f,ll *g,ll k)
    	{
    		if(k==1)
    		{
    			g[0]=qpow(f[0],mod-2);
    			return ;
    		}
    		get_inv(f,g,(k+1)>>1);
    		get_len(k);
    		for(ll i=0;i<k;i++) c[i]=f[i];
    		for(ll i=k;i<lim;i++) c[i]=0;
    		ntt(c,1);ntt(g,1);
    		for(ll i=0;i<lim;i++) g[i]=((2ll-c[i]*g[i]%mod+mod)%mod)*g[i]%mod;
    		ntt(g,-1);
    		for(ll i=k;i<lim;i++) g[i]=0;
    	}
    	
    	void get_der(ll *f,ll *g,ll k)
    	{
    		for(ll i=1;i<k;i++) g[i-1]=i*f[i]%mod;
    		g[k-1]=0;
    	}
    	
    	void get_int(ll *f,ll *g,ll k)
    	{
    		for(ll i=1;i<k;i++) g[i]=f[i-1]*qpow(i,mod-2)%mod;
    		g[0]=0;
    	}
    	
    	void get_ln(ll *f,ll *g,ll k)
    	{
    		memset(a,0,sizeof(a));
    		memset(b,0,sizeof(b));
    		get_der(f,a,k);get_inv(f,b,k);get_len(k);
    		ntt(a,1);ntt(b,1);
    		for(ll i=0;i<lim;i++) a[i]=a[i]*b[i]%mod;
    		ntt(a,-1);
    		get_int(a,g,k); 
    	}	
    	
    	void get_exp(ll *a,ll *b,ll k)
    	{
    		if(k==1)
    		{
    			b[0]=1;
    			return ;	
    		}
    		get_len(k);
    		get_exp(a,b,(k+1)>>1);
    		get_ln(b,d,k);
    		for(int i=0;i<k;i++) e[i]=a[i];
    		for(int i=k;i<lim;i++) d[i]=e[i]=0;
    		ntt(d,1);ntt(b,1);ntt(e,1);
    		for(int i=0;i<lim;i++) b[i]=(1-d[i]+e[i]+mod)%mod*b[i]%mod;
    		ntt(b,-1);
    		for(int i=k;i<lim;i++) b[i]=0;
    	}
    
        void work()
        {
           n=read();m=read();
           for(int i=1;i<=n;i++)
           {
               v[i]=read();
               cnt[v[i]]++;
           }
           for(ll i=1;i<=m;i++)
           {
               if(cnt[i])
               {
                   int cur=0;
                   for(int j=i;j<=m;j+=i)
                   {
                   	   cur++;
                       f[j]=(f[j]+qpow(cur,mod-2)*cnt[i]%mod)%mod;
                   }
               }
           }
           get_exp(f,g,m+1);
           for(ll i=1;i<=m;i++) printf("%lld
    ",g[i]);
        }
    }
    
    int main()
    {
        zzc::work();
        return 0;
    }
    
  • 相关阅读:
    深拷贝浅拷贝
    计算属性和监听,computed,watch
    字面量的引用与使用
    MYSQL 触发器
    JavaScript寻找对象方式
    JavaScript事件传播
    HTML 绑定事件
    JavaScript 中的 String()方法
    JavScript re模块
    JavScript Math函数的使用方法
  • 原文地址:https://www.cnblogs.com/youth518/p/14336832.html
Copyright © 2011-2022 走看看