【hdu 4658】Integer Partition (无序分拆数、五边形数定理)

hdu 4658 Integer Partition

题意

n分拆成若干个正整数的和,每个正整数出现小于k次,分拆方案有多少。(t<=100,n<=1e5)

题解

之前写过一篇Partition Numbers的计算,后面补充了hdu 4651的做法。就是利用五边形数定理。
这题加强了限制条件。

n的无序分拆数的生成函数:
(sum_{i=1}^{infty}B(i)x^i=(1+x+x^2+..)(1+x^2+x^4+..)…=frac {1}{prod_{i=1}^{infty}(1-x^i)})

由五边形数定理知

(prod_{n=1}^{infty}(1-x^n)=sum_{k=0}^infty (-1)^k x^{frac {k(3kpm 1)}{2}})

((1+B(1)x+B(2)x^2+..)(1-x-x^2+x^5+x^7+..)=1)

比较两边(x^n)系数,得到

(B(n)-B(n-1)-B(n-2)+B(n-5)+B(n-7)+..=0)

所以可以(O(n^{1.5}))计算出B。

有了限制,生成函数变成
(g(x)=(1+x+x^2+..+x^{k-1})(1+x^2+x^4+..+x^{2(k-1)})…= {prod_{i=1}^infty(1-x^{ik})}B(x))

再利用五边形数定理得

(g(x)=(1-x^k-x^{2k}+x^{5k}+..)(1+B(1)x+B(2)x^2+B(3)x^3+..))

(x^n)的系数即为答案。

代码

int n,k;
int B[N]={1,1,2};
int main() {
	int t;
	sf(t);
	for(int i=3;i<N;++i)
	for(int j=1,f=1;f;++j)
	for(int k=-1;k<2;k+=2){
		int w=(3*j*j+k*j)/2;
		if(w>i){f=0;break;}
		if(j%2)B[i]=(B[i]+B[i-w])%mod;
		else B[i]=(B[i]-B[i-w]+mod)%mod;
	}
	while(t--){
		sf(n);sf(k);
		int ans=0;
		ans=B[n]%mod;
		for(int i=1,f=1;f;++i)
		for(int j=-1;j<2&&f;j+=2){
			int w=(3*i*i+j*i)/2;
			if(w*k>n){f=0;break;}
			if(i%2==0)ans=(ans+B[n-w*k])%mod;
			else ans=(ans-B[n-w*k]+mod)%mod;
		}
		printf("%d
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/flipped/p/7475488.html