1. 程式人生 > >LOJ #6268. 分拆數

LOJ #6268. 分拆數

ima 技術分享 oot .com true png swa 發現 inline

技術分享圖片

可以先將問題建模為 : 物品大小為1~inf 且每件物品數量無限 的背包選體積為1~n的方案數。

顯然物品體積只有1~n有用,我們不妨把 體積1~sqrt(n) 的物品先暴力插入到背包中,設這一部分最後 體積i的方案數是 A[i] 。

考慮體積>sqrt(n)的物品怎麽計算方案,可以發現這樣的物品最多只能有sqrt(n)件。

這有什麽用呢? 當然是dp用啊! 設f[i][j] 為選了i件>sqrt(n)的物品,且總體積是j的方案數,顯然第一維只有sqrt(n),直接轉移做就行了,設這一部分最後 體積i的方案數是B[i]。

不過還有一點很惡心:我們還要合並 A[] 與 B[]。

也沒啥,寫個NTT就好了hhhh

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100005,root=3,ha=998244353,inv=ha/3+1,Base=333;
inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x;}
inline void ADD(int &x,int y){ x+=y; if(x>=ha) x-=ha;}
void W(int x){ if(x>=10) W(x/10); putchar(x%10+‘0‘);}
int A[maxn*4],F[Base+5][maxn],B[maxn*4],r[maxn*4],n,l,S,N,INV;
inline int ksm(int x,int y){ int an=1; for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha; return an;}

inline void dp(){
	A[0]=1;
	for(int i=1;i<Base;i++)
	    for(int j=i;j<=n;j++) ADD(A[j],A[j-i]);
	    
	F[0][0]=1,S=maxn/Base+1,F[1][Base]=1;
	for(int i=1;i<S;i++)
	    for(int j=0;j<=n;j++) if(F[i][j]){
	    	if(j+i<=n) ADD(F[i][j+i],F[i][j]);
	    	if(j+Base<=n) ADD(F[i+1][j+Base],F[i][j]);
	    	ADD(B[j],F[i][j]);
		}
	ADD(B[0],1);
}

inline void NTT(int *c,int f){
	for(int i=0;i<N;i++) if(i<r[i]) swap(c[i],c[r[i]]);
	
	for(int i=1;i<N;i<<=1){
		int omega=ksm((f==1?root:inv),(ha-1)/(i<<1));
		for(int P=i<<1,j=0;j<N;j+=P){
			int now=1;
			for(int k=0;k<i;k++,now=now*(ll)omega%ha){
				int x=c[j+k],y=c[j+k+i]*(ll)now%ha;
				c[j+k]=add(x,y);
				c[j+k+i]=add(x,ha-y);
			}
		}
	}
	
	if(f==-1) for(int i=0;i<N;i++) c[i]=c[i]*(ll)INV%ha;
}

int main(){
	scanf("%d",&n),dp();
	
	for(N=1;N<=(n<<1);N<<=1) l++;
	for(int i=0;i<N;i++) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	NTT(A,1),NTT(B,1);
	for(int i=0;i<N;i++) A[i]=A[i]*(ll)B[i]%ha;
	INV=ksm(N,ha-2),NTT(A,-1);
	
	for(int i=1;i<=n;i++) W(A[i]),puts("");
	return 0;
}

  

LOJ #6268. 分拆數