Loading

题解 lg4389 付公主的背包 生成函数

题意

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

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

思路

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

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

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

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

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

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

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

对数运算!

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

看到上面这个式子,我们只需要求出其前 \(n\) 项的系数即可,然后可以看出上面这个式子复杂度就是在枚举因数,调和级数保证其复杂度在 \(O(n\ln 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\n",ans[i]%MOD);
	return 0;
}
posted @ 2021-02-03 14:20  fpjo  阅读(90)  评论(0编辑  收藏  举报