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(3k\pm 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\n",ans);
}
return 0;
}
05-11 13:07