划分数求法
划分数是一个非常常见的问题。
给定一个整数
如
一、O(n^2) 算法
设把
1.完全背包
直接 DP 每个数字取了多少次
由于我们并不在意具体取了多少次,所以可以把
2.构造法
在构造法中,为了方便,我们允许划分出 0。
我们考虑一个划分序列可以怎么构造出来
- 序列后面加一个
0 。 - 把序列所有数
+1 。
容易证明,每个拆分序列都会被有且仅有一个构造方式构造出来
所以我们得到转移方程:
时间复杂度为
二、O(n\sqrt n) 算法
我们发现,当取的数字很多时,取的数字就很小;取的数字很大时,取的数字就很少。所以我们能不能结合以上两种算法呢?
我们取一个临界值
我们考虑分开处理只取
只选
只选
拼在一起是
代码:
int B=sqrt(n);
f[0]=1;
for(int i=1;i<B;i++)
for(int j=i;j<=n;j++)
f[j]=(1ll*f[j]+f[j-i])%mod;
g[0]=1;
for(int i=1;i<=n/B;i++){
for(int j=0;j<=n;j++){
if(j>=i)g[j]=(1ll*g[j]+g[j-i])%mod;
if(j+i*B<=n)ans=(1ll*ans+1ll*g[j]*f[n-(j+i*B)])%mod;
}
}
注意代码中因为要省空间所以使用了滚动数组,数组 g 交换了两维。
还有一种方法是五边形数,可是我不会qwq。