【笔记】整数拆分与五边形数
广义五边形数 \(a_i = \dfrac{3n^2 \pm n}{2}\),前几项是 \(\{1,2,5,7,12,15,22,26,35,40,\cdots\}\)。
我们记 \(f_i\) 为 \(i\) 的划分数,有递推式 \(f_i = f_{i - 1} + f_{i - 2} - f_{i-5} - f_{i - 7} + f_{i + 12} \cdots\)。
\(a\) 是平方级别增长的,所以递推项数是根号级别的,时间复杂度 \(\mathcal{O}(N\sqrt N)\)。
#define N 500005
int n, f[N], g[N], t;
int main() {
read(n, P);
int x = 1;
while(true){
g[++t] = (x * x * 3 - x) / 2;
g[++t] = (x * x * 3 + x) / 2;
x++; if(g[t] > n)break;
}
if(g[--t] > n)t--;
f[0] = 1;
rp(i, n){
rp(j, t){
if(g[j] > i)break;
if(((j + 1) / 2) & 1)ad(f[i], f[i - g[j]]);
else su(f[i], f[i - g[j]]);
}
}
printf("%d\n", f[n]);
return 0;
}
如果拆分成若干个不相等的自然数之和怎么做?
我们可以将拆分写成一个上升序列,比如 \(A = \{1,2,4,5, 7\}\),等价于 \(B= \{b_i | b_i = \sum [a_j \ge i]\}\)。
不难发现如果将 \(B\) 从小到大排序,相邻两项的差 \(\le 1\),所以我们只用考虑 \(1\sim \sqrt{N}\) 的数,做 \(\sqrt{N}\) 次完全背包即可,时间复杂度 \(\mathcal{O}(N\sqrt N)\)。
#define N 500005
int n, f[N], g[N];
int main() {
read(n);
f[0] = g[0] = 1; int l = 0;
for(int i = 1; i * (i + 1) / 2 <= n; i++){
pre(j, n - i, l)f[j + i] = f[j];
l += i;
rep(j, l, n){
ad(g[j], f[j]);
if(i + j <= n)ad(f[i + j], f[j]);
}
}
printf("%d\n", g[n]);
return 0;
}