【生成函数】五边形数定理与整数划分问题详解
Part 0. 前置知识
- 简单的公式推导
- 生成函数
大量数学公式警告
Part 1. 什么是五边形数
五边形数
看一张图吧:
XP 系统带的画图真的挺好用!
我们不难得出五边形数的数列:
P = { 1 , 5 , 12 , 22 , … } P = \{1, 5, 12, 22,\ldots\} P={1,5,12,22,…}
再找一下规律,我们就可以得到五边形数的通项公式:
P n = n ( 3 n − 1 ) 2 P_n = \frac{n(3n - 1)}{2} Pn=2n(3n−1)
似乎现在并没有什么用。。。
广义五边形数
也就是当 n n n 可以为负数的时候的五边形数序列,它的通项公式为:
P n = n ( 3 n ± 1 ) 2 P_n = \frac{n(3n \pm 1)}{2} Pn=2n(3n±1)
Part 2. 五边形数定理
定义欧拉函数(注意不要把它看成数论里面的欧拉函数)为:
ϕ ( n ) = ∏ i = 1 ∞ ( 1 − x i ) \phi (n) = \prod\limits_{i = 1}^{\infty} (1 - x^i) ϕ(n)=i=1∏∞(1−xi)
则五边形数定理就是:
ϕ ( n ) = ∏ i = − ∞ ∞ ( − 1 ) i x i ( 3 i − 1 ) 2 = 1 + ∏ i = 0 ∞ ( − 1 ) i x i ( 3 i ± 1 ) 2 \phi (n) = \prod\limits_{i = -\infty}^{\infty}(-1)^ix^{\frac{i(3i - 1)}{2}} = 1 + \prod\limits_{i = 0}^{\infty}(-1)^ix^{\frac{i(3i \pm 1)}{2}} ϕ(n)=i=−∞∏∞(−1)ix2i(3i−1)=1+i=0∏∞(−1)ix2i(3i±1)
证明等一会给出。
Part 3. Ferrers 图
Ferrers 图是一个由 k k k 行的点阵构成的方格图,要求上一行的格子数目不超过它下面一行的格子的数目,其中总和为 n n n 的数量。
举个例子:( n = 20 n = 20 n=20 时 的一个图,我用 @ 来表示一个格子)
@@@@@@
@@@@@
@@@@
@@@
@@
我们记这时的第 k k k 行的格子数量为 m m m, s s s 为第一行最右边的 @ 右上角的对角线格子数量。
定义一种变换:
- 当 m > s m > s m>s 时, 把最右边的对角线上的格子扔到新的一行。
- 当 m ≤ s m \le s m≤s 时,把最后一行的所有格子分给从第一行开始的每行上,每行分到一个格子。
举个例子:(用 $ 表示被移走的格子)
@@@@@@@@ @@@@@@@@$
@@@@@@@ @@@@@@@$
@@@@@ <=> @@@@@
@@@ @@@
$$
不难看出这个操作是显然可逆的。
这个操作还有一些性质:
我们对一个 Ferrers 图进行连续的两次变换后,它会变回它本身。
如果我们仅进行一次变换,那么行数的奇偶性可以改变。
Part 4. 五边形数定理的证明
我们将 ϕ ( n ) \phi(n) ϕ(n) 的定义式展开之后可以发现,第 n n n 项的系数应该是将 n n n 划分成偶数个互不相同的正整数的答案减掉将 n n n 划分成奇数个互不相同的正整数方案,每一项的系数应该都是 0 0 0。但实际操作后发现有些项前面是有系数的。
那么我们考虑仔细研究一下 Ferrers 图。
当 m = s + 1 m = s + 1 m=s+1 时,且底层的最右边的元素与对角线相遇,如下:
@@@@@@ @@@@@
@@@@@ => @@@@
@@@@ @@@@@@
发现此时无法操作回去了。这是一个不合法的状态。
令 t = 1 − m t = 1 - m t=1−m,可以得到 n = ( 1 − t ) + ( 2 − t ) + ( 3 − t ) + ⋯ + ( − 2 t ) = t ( 3 t − 1 ) 2 n = (1 - t) + (2 - t) + (3 - t) + \cdots + (-2t) = \frac{t(3t - 1)}{2} n=(1−t)+(2−t)+(3−t)+⋯+(−2t)=2t(3t−1),这一项对答案的贡献为 ( − 1 ) t (-1) ^ t (−1)t。
当 m = s m = s m=s,且底层的最右边的元素与对角线相遇,如下:
@@@@@ @@@@@@
@@@@ => @@@@@
@@@ @
这个显然是不合法的,令 t = m t = m t=m,可以得到 n = t + ( t + 1 ) + ( t + 2 ) + ⋯ + 2 t = t ( 3 t + 1 ) 2 n = t + (t + 1) + (t + 2) + \cdots + 2t = \frac{t(3t + 1)}{2} n=t+(t+1)+(t+2)+⋯+2t=2t(3t+1),对第 n n n 项的贡献为 ( − 1 ) t (-1) ^ t (−1)t。
这样一来,当 n = k ( 3 k ± 1 ) 2 n = \frac{k(3k \pm 1)}{2} n=2k(3k±1) 时,即 n n n 是一个广义五边形数时, n n n 的奇偶拆分就在抵消之后留下一项。所以五边形定理成立。
Part 5. 整数划分问题
问题概要
将一个整数 n n n 划分成任意个可以相等的整数,求方案数。
分析
我们不难写出一个 O ( n 2 ) O(n^2) O(n2) 的 DP 来解决整数划分问题。但当 n n n 超过 1 0 4 10^4 104 之后就跑不动了。
考虑高效解法 (利用五边形数定理)
定义函数 P ( i ) P(i) P(i) 为将 n n n 拆分成若干个可以相等的整数的方案数。
考虑 P ( i ) P(i) P(i) 的生成函数 F ( x ) F(x) F(x):
F ( x ) = ∑ i = 1 ∞ P ( i ) x i = ∏ i = 1 ∞ ( x 0 + x i + x 2 i + ⋯ ) = ∏ i = 1 ∞ 1 1 − x i \begin{aligned} F(x) & = \sum\limits_{i = 1}^{\infty}P(i)x^i \\ &= \prod\limits_{i = 1}^{\infty}(x^0 + x^i + x^{2i} + \cdots) \\ & =\prod\limits_{i = 1}^{\infty} \frac{1}{1 - x^i} \end{aligned} F(x)=i=1∑∞P(i)xi=i=1∏∞(x0+xi+x2i+⋯)=i=1∏∞1−xi1
再看一下上面提到的欧拉函数:
ϕ ( x ) = ∏ i = 1 ∞ ( 1 − x i ) \phi(x) = \prod\limits_{i = 1}^{\infty} (1 - x^i) ϕ(x)=i=1∏∞(1−xi)
显然可以得到: F ( x ) ⋅ ϕ ( x ) = 1 F(x)\cdot\phi(x) = 1 F(x)⋅ϕ(x)=1。
然后根据五边形数定理将 ϕ ( x ) \phi(x) ϕ(x) 代入:
[ ∑ i = 1 ∞ P ( i ) x i ] ⋅ ( ∑ i = 0 ∞ 1 + ∏ i = 0 ∞ ( − 1 ) i x i ( 3 i ± 1 ) 2 ) = 1 \left[\sum\limits_{i = 1}^{\infty}P(i)x^i\right] \cdot \left(\sum\limits_{i = 0}^{\infty}1 + \prod\limits_{i = 0}^{\infty}(-1)^ix^{\frac{i(3i \pm 1)}{2}}\right) = 1 [i=1∑∞P(i)xi]⋅(i=0∑∞1+i=0∏∞(−1)ix2i(3i±1))=1
直接暴力展开可以得到:
P ( n ) − P ( n − 1 ) − P ( n − 2 ) + P ( n − 5 ) + P ( n − 7 ) − ⋯ = 0 P(n) - P(n - 1) - P(n - 2) + P(n - 5) + P(n - 7) - \cdots = 0 P(n)−P(n−1)−P(n−2)+P(n−5)+P(n−7)−⋯=0
不难发现减去的每一项都是广义五边形数。于是直接暴力计算即可。
由于五边形数 p n = n ( 3 n ± 1 ) 2 p_n = \frac{n(3n \pm 1)}{2} pn=2n(3n±1) 的增长速度为 O ( n 2 ) O(n ^ 2) O(n2)。所以最多枚举不超过 O ( n ) O(\sqrt n) O(n ) 项五边形数就可以退出循环,故时间复杂度为 O ( n n ) O(n\sqrt n) O(nn )。
Part 6. 例题
HDU 4651
题目大意
求将正整数 n n n 划分成多个正整数的和的方案数。
分析
就是一个模板了。。。好好看一看上面的就可以了。
HDU 4658
题目大意
将一个正整数 n n n 划分为多个正整数的和的方案数,但每个数的使用次数不能够大于等于 k k k 次。
分析
记这种方式下的划分数为 P ′ ( n ) P'(n) P′(n)。先给出生成函数 G ( x ) = ∏ i = 1 ∞ ( 1 + x i + x 2 i + ⋯ + x ( k − 1 ) i ) G(x) = \prod\limits_{i = 1}^{\infty}(1 + x^i + x^{2i} + \cdots + x^{(k - 1)i}) G(x)=i=1∏∞(1+xi+x2i+⋯+x(k−1)i)。
尝试化简:
G ( x ) = ∏ i = 1 ∞ 1 + x i + x i + x i + ⋯ 1 + x k i + x 2 k i + x 3 k i + ⋯ = ∏ i = 1 ∞ 1 1 − x i 1 1 − x k i = ∏ i = 1 ∞ 1 − x k i 1 − x i = ϕ ( x k ) ϕ ( x ) = ϕ ( x k ) ⋅ F ( x ) \begin{aligned} G(x) & = \prod\limits_{i = 1}^{\infty}\frac{1+ x^i + x^i + x^i + \cdots}{1 + x^{ki} + x^{2ki} + x^{3ki} + \cdots} \\ & = \prod_{i = 1}^{\infty}\frac{\frac{1}{1 - x^i}}{\frac{1}{1 - x^{ki}}} \\ & = \prod_{i = 1}^{\infty}\frac{1 - x^{ki}}{1 - x^i} \\ & = \frac{\phi(x^k)}{\phi(x)} \\ & = \phi(x^k) \cdot F(x) \end{aligned} G(x)=i=1∏∞1+xki+x2ki+x3ki+⋯1+xi+xi+xi+⋯=i=1∏∞1−xki11−xi1=i=1∏∞1−xi1−xki=ϕ(x)ϕ(xk)=ϕ(xk)⋅F(x)
然后暴力展开 ϕ ( x k ) ⋅ F ( x ) \phi(x^k) \cdot F(x) ϕ(xk)⋅F(x) 得到:
∑ i = 1 ∞ P ′ ( i ) x i = ( 1 − x k − x 2 k + x 5 k + ⋯ ) [ 1 + P ( 1 ) x + P ( 2 ) x 2 + ⋯ ] \sum\limits_{i = 1}^{\infty}P'(i)x^i = (1 - x^k - x^{2k} + x^{5k} + \cdots)[1 + P(1)x + P(2)x^2 + \cdots] i=1∑∞P′(i)xi=(1−xk−x2k+x5k+⋯)[1+P(1)x+P(2)x2+⋯]
然后可以得到 P ′ ( i ) = P ( i ) − P ( i − k ) − P ( i − 2 k ) + P ( i − 5 k ) + ⋯ P'(i) = P(i) - P(i - k) - P(i - 2k) + P(i - 5k) + \cdots P′(i)=P(i)−P(i−k)−P(i−2k)+P(i−5k)+⋯
于是就可以在 O ( n ) O(\sqrt n) O(n ) 的时间内回答询问了,总时间复杂度为 O ( n n + T n ) O(n\sqrt n + T\sqrt n) O(nn +Tn )。
参考代码
HDU 4651
#include <cstdio>
#include <algorithm>
using namespace std;typedef long long ll;
const int Maxn = 1e5;
const ll Mod = 1000000007;ll f[Maxn * 2 + 5];
ll p[Maxn + 5];
void Init() {for(int i = -Maxn; i <= Maxn; i++)f[i + Maxn] = 1LL * i * (i * 3 - 1) / 2;//计算五边形数p[0] = 1;for(int i = 1; i <= Maxn; i++)for(int j = 1; j <= i; j++) {if(f[j + Maxn] <= i) {if(j & 1) p[i] = (p[i] + p[i - f[j + Maxn]]) % Mod;else p[i] = (p[i] - p[i - f[j + Maxn]] + Mod) % Mod;} else break;//根据 j 的奇偶性来判断当前应该加还是应该减if(f[Maxn - j] <= i) {if(j & 1) p[i] = (p[i] + p[i - f[Maxn - j]]) % Mod;else p[i] = (p[i] - p[i - f[Maxn - j]] + Mod) % Mod;} else break;}
}int main() {#ifdef LOACLfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifInit();int _;scanf("%d", &_);while(_--) {int n;scanf("%d", &n);printf("%lld\n", p[n]);}return 0;
}
HDU 4658
#include <cstdio>
#include <algorithm>
using namespace std;typedef long long ll;
const int Maxn = 1e5;
const ll Mod = 1e9 + 7;ll f[Maxn * 2 + 5];
ll p[Maxn + 5];
ll ans[Maxn + 5];
void Init() {for(int i = -Maxn; i <= Maxn; i++)f[i + Maxn] = 1LL * i * (3 * i - 1) / 2;p[0] = 1;for(int i = 1; i <= Maxn; i++)for(int j = 1; j <= i; j++) {if(f[j + Maxn] <= i) {if(j & 1) p[i] = (p[i] + p[i - f[j + Maxn]]) % Mod;else p[i] = (p[i] - p[i - f[j + Maxn]] + Mod) % Mod;} else break;if(f[Maxn - j] <= i) {if(j & 1) p[i] = (p[i] + p[i - f[Maxn - j]]) % Mod;else p[i] = (p[i] - p[i - f[Maxn - j]] + Mod) % Mod;} else break;}
}ll Solve(int n, int k) {ll ret = p[n], dir = -1;for(int i = 1; ; i++) {ll val1 = 1LL * k * i * (3 * i - 1) / 2,val2 = 1LL * k * i * (3 * i + 1) / 2;if(val1 > n && val2 > n) break;if(val1 <= n) ret = (ret + dir * p[n - val1] + Mod) % Mod;if(val2 <= n) ret = (ret + dir * p[n - val2] + Mod) % Mod;dir *= -1;}return ret;
}int main() {#ifdef LOACLfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifInit();int _;scanf("%d", &_);while(_--) {int n, k;scanf("%d %d", &n, &k);printf("%lld\n", Solve(n, k));}return 0;
}
【生成函数】五边形数定理与整数划分问题详解相关推荐
- 力扣13 罗马数字转整数逻辑详解
力扣 13 .罗马数字转整数逻辑详解 题目详情 逻辑分析 代码实现 题目详情 罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 1 ...
- 算法问题:整数除法详解(Java方向)
算法问题:整数除法详解(Java方向) 1.力扣题目 2.结果代码分析 3.完整的结果代码 4.代码出处和教学出处 5.博主 边学习边记录算法的学习 1.力扣题目 1.给定两个整数 a 和 b ,求它 ...
- 如何判断立即数的合法性?(详解详析)
如何判断立即数的合法性?(详解详析) <ARM体系结构与编程>一书中对立即数有这样的描述:每个立即数由一个8位的常数循环右移偶数位得到. 一个32位的常数,只有能够通过上面构造方法得到的才 ...
- 五边形数定理与拆分数
只做梳理,不做证明 (因为不会证) 五边形数 图片摘自百度百科. 可以发现, g i = g i − 1 + 3 ( i − 1 ) + 1 g_i=g_{i-1}+3(i-1)+1 gi=gi−1 ...
- hive linux进程数,控制Hive MAP个数详解
控制Hive MAP个数详解 Hive的MAP数或者说MAPREDUCE的MAP数是由谁来决定的呢?inputsplit size,那么对于每一个inputsplit size是如何计算出来的,这是做 ...
- 数通--交互技术--STP+RSTP详解
交互技术–STP+RSTP详解 STP: 一.作用: 1.解决环路(广播风暴.Mac地址表震荡) 2.实现链路备份的目的 二.stp流程(原理): 1.选举一个根桥 (优先级=BID<0-655 ...
- 【原】费马小定理(Fermat little theorem)详解
Fermat定理: 如果P是任意一个不能整除整数a的素数,则 之后我会展示一些用到这一经典定理的算法. 例如: , 等等 证明: 考虑a的倍数: (1) 证明这些整数中任意两个都不能模p同余. ...
- C语言经典回溯算法之解决数的组合问题(详解)
文章目录 一.回溯算法 二.数的组合问题 一.回溯算法 1.回溯法 也叫试探法,实际上是一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就"回溯&q ...
- 200 300的完数 c语言,C语言求完数(完全数)(详解版)
问题描述 求某一范围内完数的个数. 如果一个数等于它的因子之和,则称该数为"完数"(或"完全数").例如,6的因子为1.2.3,而 6=1+2+3,因此6是&q ...
最新文章
- 使用AngularJS上传文件
- Apache与Tomcat整合
- lua运行外部程序_LTUI v2.2 发布, 一个基于lua的跨平台字符终端UI界面库
- Python正则表达式笔记
- 玩转运维编排服务的权限:Assume Role+Pass Role
- java连接kafka接收不到数据_Kafka客户端无法接收消息
- jQuery 1.11 / 2.1 beta 版发布
- 在新窗口中打开链接 javascript
- Json Formatter 1.0 Json格式化工具
- linux centos erlang,CentOS 7.7安装Erlang和Elixir
- Java-JVM第一篇认识JVM
- vs2008/vs2010新手快速入门必读教程
- hmcl手机版_hmcl启动器app下载
- ch2 gpio应用:Buzzer封装
- 微信小程序-icon图标
- 五子棋AI算法(一)
- 传奇清理服务器信息,传奇行会信息等清除问题
- 导入导出专栏(poi,jxls)
- 火狐linux 32位,火狐浏览器下载电脑版32位
- 2015阿里校园招聘笔试题(8.29 测试开发工程师)
热门文章
- 企业老总亲自回答:计算机视觉方向今年招聘情况怎么样?是否已经人才过剩?...
- 西安Java培训 | java设计模式之工厂设计模式
- 李亚涛:python中文如何转换成拼音?
- React 官方纪录片:我们亲手将 JSX 推向神坛!
- linearlayout靠右显示
- 【QT从零开始系列12】QT学习心得及资源汇总
- linux恢复bios出厂设置密码,笔记本bios恢复出厂设置方法【图文详解】
- Ubuntu16.04配置高端霸气VIM
- 等待队列、工作队列、消息队列的区别和用法
- xp系统打印机服务器不可用,xp系统使用打印机服务提示“打印不成功”的步骤介绍...