专栏文章
划分数求法
算法·理论参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mioihtbx
- 此快照首次捕获于
- 2025/12/02 19:45 3 个月前
- 此快照最后确认于
- 2025/12/02 19:45 3 个月前
划分数是一个非常常见的问题。
给定一个整数 ,求有多少种划分方式。
如 。
一、 算法
设把 划分成 个数有 种分法。
1.完全背包
直接 DP 每个数字取了多少次
由于我们并不在意具体取了多少次,所以可以把 去掉。时间复杂度为 。 为数字种类数。
2.构造法
在构造法中,为了方便,我们允许划分出 0。
我们考虑一个划分序列可以怎么构造出来
- 序列后面加一个 。
- 把序列所有数 。
容易证明,每个拆分序列都会被有且仅有一个构造方式构造出来
所以我们得到转移方程:
时间复杂度为 。 为拆分序列最大长度。
二、 算法
我们发现,当取的数字很多时,取的数字就很小;取的数字很大时,取的数字就很少。所以我们能不能结合以上两种算法呢?
我们取一个临界值 。
我们考虑分开处理只取 的情况和只取 的情况。再把两边的答案拼在一起就是最终答案。
只选 时,取的数字很小,用背包的方法就能 求出答案。
只选 时,取的数字很少,不会超过 个,用构造法就能 求出答案。
拼在一起是 的,所以总的时间复杂度为 ,。
代码:
CPPint 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。
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...