1. 程式人生 > 實用技巧 >【洛谷4389】付公主的揹包(多項式Exp)

【洛谷4389】付公主的揹包(多項式Exp)

點此看題面

  • \(n\)種物品和一個大小為\(m\)的揹包。
  • 每種物品都有無限個,其中第\(k\)種物品體積為\(v_k\)
  • 對於\([1,m]\)中的每一種體積\(i\),求出選擇物品總體積恰好為\(i\)的方案數。
  • \(n,m\le10^5\)

生成函式

考慮對於第\(i\)種物品,我們可以得出它的生成函式:

\[F_i(x)=\sum_{k=0}^{+\infty}x^{k\times v_i}=\frac 1{1-x^{v_i}} \]

一種暴力的做法就是將\(n\)個生成函式全部捲起來,但這顯然直接\(T\)飛。

眾所周知,有一種很好的把乘法變成加法的方式,就是取對數。

也就是說:

\[Ans(x)=\prod_{i=1}^nF_i(x)=e^{\sum_{i=1}^n\ln F_i(x)} \]

然而,對每個生成函式都暴力求一次對數複雜度依舊堪憂。

但是啊,考慮到我們求的是\(\frac1{1-x^{v_i}}\)的對數,顯然有\(\ln\frac 1{1-x^{v_i}}=-\ln(1-x^{v_i})\),而\(\ln(1-x^a)\)是有一個簡便的計算公式的。

\(\ln(1-x^a)\)

\(G(x)=\ln(1-x^a)\),首先兩邊同時求導:

\[G'(x)=\frac{(1-x^a)'}{1-x^a}=\frac{-a\times x^{a-1}}{1-x^a} \]

反向利用等比數列求和公式把\(1-x^a\)化回去得到:

\[G'(x)=-a\times x^{a-1}\times\sum_{k=0}^{+\infty}x^{a\times k}=-\sum_{k=0}^{+\infty}a\times x^{a\times(k+1)-1} \]

不妨令列舉的\(k\)\(1\),因此就有:

\[G'(x)=-\sum_{k=1}^{+\infty}ax^{ak-1} \]

接著我們把\(G'(x)\)還原回\(G(x)\),得到:

\[G(x)=-\sum_{k=1}^{+\infty}\frac1{ak}\times ax^{ak}=-\sum_{k=1}^{+\infty}\frac1kx^{ak} \]

總結一下,也就是說:

\[\ln (1-x^a)=-\sum_{k=1}^{+\infty}\frac1kx^{ak} \]

求解最終答案

我們把\(\ln (1-x^a)=-\sum_{k=1}^{+\infty}\frac1kx^{ak}\)這一式子代回去,於是:

\[\ln F_i(x)=\sum_{k=1}^{+\infty}\frac1kx^{v_ik} \]

顯然,因為我們最後只要知道答案多項式的前\(m\)項,那麼對於每一個\(\ln F_i(x)\)也只用保留前\(m\)項即可。

不過現在我們要的是\(e^{\sum_{i=1}^n\ln F_i(x)}\),只要先求出\(\sum_{i=1}^n\ln F_i(x)\)再做一個多項式\(Exp\)即可。

至於\(\sum_{i=1}^n\ln F_i(x)\),我們可以統計\(cnt_x\)表示有多少\(v_i=x\),然後對於每個\(x\)\(\lfloor\frac mx\rfloor\)的複雜度去修改一遍最終多項式的係數,那麼總複雜度就是調和級數。

程式碼:\(O(mlogm)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define X 998244353
using namespace std;
int n,m,f[N+5],g[N+5],c[N+5],Inv[N+5];
I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define pc(c) (C==E&&(clear(),0),*C++=c)
		#define D isdigit(c=tc())
		int T;char c,*A,*B,*C,*E,FI[FS],FO[FS],S[FS];
	public:
		I FastIO() {A=B=FI,E=(C=FO)+FS;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
		Tp I void writeln(Ty x) {W(S[++T]=x%10+48,x/=10);W(T) pc(S[T--]);pc('\n');}
		I void clear() {fwrite(FO,1,C-FO,stdout),C=FO;}
}F;
namespace Poly//多項式板子
{
	#define Init(n) P=1,L=0;W(P<=2*(n)) P<<=1,++L;\
		for(i=0;i^P;++i) A[i]=B[i]=0,R[i]=((R[i>>1]>>1)|((i&1)<<L-1));
	int PR=3,P,L,R[N<<2],p[N+5],A[N<<2],B[N<<2];
	I void NTT(int* s,CI op)
	{
		RI i,j,k,x,y,U,S;for(i=0;i^P;++i) i<R[i]&&(x=s[i],s[i]=s[R[i]],s[R[i]]=x);
		for(i=1;i^P;i<<=1) for(U=QP(QP(PR,op),(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1,k=0;
			k^i;++k,S=1LL*S*U%X) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X;
	}
	I void Inv(CI n,int* a,int* b)
	{
		if(!n) return (void)(b[0]=QP(a[0],X-2));RI i;Inv(n>>1,a,b);
		Init(n);for(i=0;i<=n;++i) A[i]=a[i],B[i]=b[i];
		for(NTT(A,1),NTT(B,1),i=0;i^P;++i) A[i]=(2LL*B[i]-1LL*A[i]*B[i]%X*B[i]%X+X)%X;
		RI t=QP(P,X-2);for(NTT(A,X-2),i=0;i<=n;++i) b[i]=1LL*A[i]*t%X;
	}
	I void Ln(CI n,int* a,int* b)
	{
		RI i;for(i=0;i<=n;++i) b[i]=0;Inv(n,a,b);
		Init(n-1);for(i=0;i<=n-1;++i) A[i]=1LL*a[i+1]*(i+1)%X,B[i]=b[i];
		for(NTT(A,1),NTT(B,1),i=0;i^P;++i) A[i]=1LL*A[i]*B[i]%X;
		RI t=QP(P,X-2);for(NTT(A,X-2),b[0]=i=0;i^n;++i) b[i+1]=1LL*A[i]*t%X*QP(i+1,X-2)%X;
	}
	int q[N+5];I void Exp(CI n,int* a,int* b)//多項式Exp
	{
		if(!n) return (void)(b[0]=1);RI i;Exp(n>>1,a,b);
		Ln(n,b,p),Init(n);for(i=0;i<=n;++i) A[i]=b[i],B[i]=(!i-p[i]+a[i]+X)%X;
		for(NTT(A,1),NTT(B,1),i=0;i^P;++i) A[i]=1LL*A[i]*B[i]%X;
		RI t=QP(P,X-2);for(NTT(A,X-2),i=0;i<=n;++i) b[i]=1LL*A[i]*t%X;
	}
}
int main()
{
	RI i,j,x;for(F.read(n),F.read(m),i=1;i<=n;++i) F.read(x),++c[x];//統計每種v[i]的個數
	for(Inv[0]=Inv[1]=1,i=2;i<=m;++i) Inv[i]=1LL*(X-X/i)*Inv[X%i]%X;//線性求逆元
	for(i=1;i<=m;++i) if(c[i]) for(j=1;i*j<=m;++j) f[i*j]=(1LL*c[i]*Inv[j]+f[i*j])%X;//調和級數複雜度求出係數
	for(Poly::Exp(m,f,g),i=1;i<=m;++i) F.writeln(g[i]);return F.clear(),0;//多項式Exp後輸出答案
}