五边形数与整数拆分问题
StudyingFather · · 题解
这次 NOI Online 入门组考了一道裸的整数拆分问题。
SF 实在太菜,只会暴力,不会这题的正解。
这个菜鸡决定查些资料,好好研究一下整数拆分问题背后的那些事情。
Warning:阅读本文需要一些生成函数与形式幂级数的相关知识,如果您还不了解的话,推荐先阅读 铃悬的数学小讲堂——生成函数初步。
Note:如果题解界面公式炸了,更好的阅读体验点 这里。
1 五边形数
五边形数(Pentagonal number)
简单来说,大概就是一个边长
从图中很容易看出递推式:
根据这个递推式,很容易得出它的通项式:
五边形数在 OEIS 上的编号是 A000326。
五边形数的性质很多,这里简单列出几条:
- 任何正整数都可以表示为不超过
5 个五边形数的和(详见 费马多边形数定理)。事实上绝大多数正整数都可以拆分为3 个五边形数的和。 - 前
n 个五边形数的平均数是第n 个三角形数。
2 广义五边形数
上面的式子中
于是我们按照
这个数列是在原来五边形数的基础上扩充范围得到的,于是我们称其为广义五边形数。它在 OEIS 上的编号为 A001318。
为了方便,广义五边形数仍然用
广义五边形数和下面要讲到的整数拆分问题有很大联系,我们在下文将会深入探究。
3 五边形数定理
介绍一个新函数:欧拉函数。
(注意:这里的欧拉函数
将这个式子展开(这个展开首先由欧拉完成):
注意
将得到的所有项按升幂排列,得到:
这个展开式的证明太神仙了,笔者并不会,感兴趣的可以看 visit_world 大爷的博客。
得到这个式子有什么用呢?它和我们接下来要提到的整数拆分问题有很大关系。
4 整数拆分问题
接下来进入重头戏。
我们来回顾一下刚刚结束的 NOI Online 里考到的 这道题:
求将正整数
n 拆分为若干个正整数的和(允许同一个数使用多次,这里的拆分是无序的,即1+2 和2+1 等价)的方案数。
本题有很多做法,时间效率不尽相同。下面介绍一种运用生成函数的方法。
下面设
考虑
用五边形数定理展开一下,整理下式子:
现在我们想要求出
将整个式子展开,得到(展开过程这里略去,感兴趣的读者这里可以自己尝试):
(PS:有的人可能对这个过程有点疑惑,这里稍微讲一下。上面的等式成立,意味着
广义五边形数再一次出现在我们的式子里了。
根据上面这个式子,我们就可以递推计算
因为广义五边形数是
嗯,代码实现挺短的:
#include <iostream>
using namespace std;
long long f[100005];
int a(int x)
{
return (3*x*x-x)/2;
}
int main()
{
int n,p;
cin>>n>>p;
f[0]=1;
for(int i=1;i<=n;i++)
for(int j=1;;j++)
{
int x=a(j),y=a(-j);
if(x<=i)
f[i]=((f[i]+(j&1?1:-1)*f[i-x])%p+p)%p;
if(y<=i)
f[i]=((f[i]+(j&1?1:-1)*f[i-y])%p+p)%p;
if(x>i||y>i)break;
}
cout<<f[n]<<endl;
return 0;
}
Reference
- 五角数 - 维基百科
- 五边形数定理 - 维基百科
- 整数分拆 - 维基百科
- 五边形数定理的一种证明_visit_world的博客