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

    题意

    (n) 种物品,体积为 (v_i) 每种物品的的数量有无限个,给定 (m) ,对于 (sin[1,m]) 求用这些商品恰好装 (s) 体积的方案数

    数据范围:(1le n,mle 10^5)

    思路

    我们会有一个非常naive的完全背包的做法,然后只能拿30pts

    发现这是一个计数类问题,统计恰好装 (s) 体积,考虑用生成函数来搞一搞

    设体积为 (v_i) 的商品 的普通生成函数 (F_{v_i}=sum_{jge0}x^{jv_i})

    记答案的普通生成函数为 (G) , (v_i) 相同的商品共(a_{v_i})个,(v_i)的集合为(S)

    [egin{aligned}G&=Pi_{v_iin S}F^{a_{v_i}}_{v_i}\&=Pi_{v_iin S}(frac{1}{1-x^{v_i}})^{a_{v_i}}end{aligned} ]

    但是复杂度仍然很高,究其原因是我们要进行多次多项式相乘

    那么有什么办法能把相乘变成相加的?

    对数运算!

    (egin{aligned}ln(G)&=-sum_{v_iin S}a_{v_i}ln(1-v_i)\&=sum_{a_{v_i}>0}a_{v_i}sum_{jge 1}frac{x^{ij}}{j}end{aligned})

    看到上面这个式子,我们只需要求出其前 (n) 项的系数即可,然后可以看出上面这个式子复杂度就是在枚举因数,调和级数保证其复杂度在 (O(nln n)) ,下面是代码实现

    for(int i=1;i<=m;i++){
    		if(!a[i])continue;
    		for(int j=1;i*j<=m;j++){
    			b[i*j]=(b[i*j]+a[i]*ninv[j])%MOD;
    		}
    	}
    

    之后再多项式exp一波就好了

    时间复杂度(O(nlogn))

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int const MAXN=2e6+10,MOD=998244353,gi=3,gf=332748118,inv2=499122177;
    int n,Len,m;
    int f[MAXN],re[MAXN],b0[MAXN],b[MAXN],a[MAXN],c[MAXN],ninv[MAXN];
    int x[MAXN],y[MAXN],A[MAXN],B[MAXN],inv[MAXN],la[MAXN],ln[MAXN];
    int sb[MAXN],sa[MAXN],ea[MAXN],pa[MAXN],pb[MAXN],ia[MAXN],ans[MAXN];
    int read(){
    	int x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    int qpow(int x,int b){
    	int ans=1,mul=x;
    	while(b){
    		if(b&1)ans=1ll*ans*mul%MOD;
    		mul=1ll*mul*mul%MOD;
    		b>>=1;
    	}
    	return ans;
    }
    void NTT(int *a,int Len,int type){
    	for(int i=0;i<Len;i++)if(i<re[i])swap(a[i],a[re[i]]);
    	for(int mid=1;mid<Len;mid<<=1){
    		int Wn=qpow(type==1?gi:gf,(MOD-1)/(mid<<1));
    		for(int l=0;l<Len;l+=(mid<<1)){
    			int w=1;
    			for(int k=0;k<mid;k++,w=1ll*w*Wn%MOD){
    				int x=a[l+k],y=1ll*a[l+k+mid]*w%MOD;
    				a[l+k]=(x+y)%MOD;a[l+k+mid]=(x-y+MOD)%MOD;
    			}
    		}
    	}
    	if(type==-1){
    		int inv=qpow(Len,MOD-2);
    		for(int i=0;i<Len;i++)a[i]=1ll*a[i]*inv%MOD;
    	}
    }
    void Init(int n){
    	int N=0;
    	for(Len=1;Len<=(n<<1);Len<<=1,N++);
    	for(int i=0;i<Len;i++)re[i]=(re[i>>1]>>1)|((i&1)<<(N-1));
    }
    void get_Inv(int *f,int n,int *b){
    	if(n==1){
    		return b[0]=qpow(f[0],MOD-2),void();
    	}
    	get_Inv(f,(n+1)>>1,b);
    	Init(n);
    	for(int i=0;i<n;i++)ia[i]=f[i];
    	for(int i=n;i<Len;i++)ia[i]=0;
    	NTT(ia,Len,1);NTT(b,Len,1);
    	for(int i=0;i<Len;i++)b[i]=1ll*(2-1ll*ia[i]*b[i]%MOD+MOD)%MOD*b[i]%MOD;
    	NTT(b,Len,-1);
    	for(int i=n;i<Len;i++)b[i]=0;
    	return;
    }
    void get_Ln(int *f,int n,int *b){
    	memset(inv,0,sizeof(inv));
    	memset(la,0,sizeof(la));
    	for(int i=0;i<=n-2;i++)la[i]=1ll*f[i+1]*(i+1)%MOD;
    	la[n-1]=0;
    	get_Inv(f,n,inv);
    	Init(n);
    	NTT(la,Len,1);NTT(inv,Len,1);
    	for(int i=0;i<Len;i++)b[i]=1ll*la[i]*inv[i]%MOD;
    	NTT(b,Len,-1);
    	for(int i=n-1;i>=1;i--)b[i]=1ll*b[i-1]*qpow(i,MOD-2)%MOD;
    	b[0]=0;
    }
    void get_Exp(int *f,int deg,int *b){
    	if(deg==1)return b[0]=1,void();
    	get_Exp(f,(deg+1)>>1,b);
    	memset(ln,0,sizeof(ln));
    	memset(ea,0,sizeof(ea));
    	get_Ln(b,deg,ln);
    	Init(deg);
    	for(int i=0;i<deg;i++)ea[i]=f[i];
    	for(int i=deg;i<Len;i++)ea[i]=0;
    	NTT(ea,Len,1);NTT(b,Len,1);NTT(ln,Len,1);
    	for(int i=0;i<Len;i++)b[i]=1ll*(1-ln[i]+ea[i]+MOD)%MOD*b[i]%MOD;
    	NTT(b,Len,-1);
    	for(int i=deg;i<Len;i++)b[i]=0;
    }
    int Add(int x,int y){
    	long long z=x+y;
    	if(z>=MOD)z-=MOD;
    	return (int)z;
    }
    signed main(){
    	n=read(),m=read();
    	ninv[1]=1;
    	for(int i=2;i<=max(n,m);i++)ninv[i]=1ll*(MOD-MOD/i)*ninv[MOD%i]%MOD;
    	for(int i=1;i<=n;i++){a[read()]++;}
    	for(int i=1;i<=m;i++){
    		if(!a[i])continue;
    		for(int j=1;i*j<=m;j++){
    			b[i*j]=(b[i*j]+a[i]*ninv[j])%MOD;
    		}
    	}
    	get_Exp(b,m+1,ans);
    	for(int i=1;i<=m;i++)printf("%lld
    ",ans[i]%MOD);
    	return 0;
    }
    
  • 相关阅读:
    C# 实现将 PDF 转文本的功能
    推荐大家使用的CSS书写规范、顺序
    Android 应用开发者必看的 9 个 Tips
    网站统计中的数据收集原理及实现
    JSON劫持漏洞攻防原理及演练
    巧妙使用 CSS3 的褪色和动画效果制作消息提醒框
    20条常见的编码陷阱
    11 个最佳 jQuery 滚动条插件
    JavaScript客户端MVC 框架综述
    20个初学者实用的CSS技巧
  • 原文地址:https://www.cnblogs.com/fpjo/p/14366805.html
Copyright © 2011-2022 走看看