核心思路
找到分割函数的生成函数 :分割函数
p ( n ) p(n) p ( n ) (将
n n n 拆分为若干正整数之和的方案数)的生成函数
P ( x ) P(x) P ( x ) 是:
P ( x ) = ∑ n = 0 ∞ p ( n ) x n = ∏ k = 1 ∞ 1 1 − x k P(x) = \sum_{n=0}^{\infty} p(n)x^n = \prod_{k=1}^{\infty} \frac{1}{1-x^k} P ( x ) = ∑ n = 0 ∞ p ( n ) x n = ∏ k = 1 ∞ 1 − x k 1
我们的目标是计算这个
P ( x ) P(x) P ( x ) 在
( m o d x N + 1 ) \pmod{x^{N+1}} ( mod x N + 1 ) 意义下的系数,即
p ( 0 ) , p ( 1 ) , … , p ( N ) p(0), p(1), \dots, p(N) p ( 0 ) , p ( 1 ) , … , p ( N ) 。
利用多项式求逆 :
P ( x ) P(x) P ( x ) 可以看作是另一个多项式
A ( x ) A(x) A ( x ) 的倒数:
P ( x ) = 1 A ( x ) 其中 A ( x ) = ∏ k = 1 ∞ ( 1 − x k ) P(x) = \frac{1}{A(x)} \quad \text{其中} \quad A(x) = \prod_{k=1}^{\infty} (1-x^k) P ( x ) = A ( x ) 1 其中 A ( x ) = ∏ k = 1 ∞ ( 1 − x k )
如果我们能快速计算出
A ( x ) ( m o d x N + 1 ) A(x) \pmod{x^{N+1}} A ( x ) ( mod x N + 1 ) ,我们就可以用一次
O ( N log N ) O(N \log N) O ( N log N ) 的
多项式求逆 来得到
P ( x ) ( m o d x N + 1 ) P(x) \pmod{x^{N+1}} P ( x ) ( mod x N + 1 ) 。
使用欧拉五边形数定理 :
直接计算
A ( x ) = ( 1 − x ) ( 1 − x 2 ) … ( 1 − x N ) ( m o d x N + 1 ) A(x) = (1-x)(1-x^2)\dots(1-x^N) \pmod{x^{N+1}} A ( x ) = ( 1 − x ) ( 1 − x 2 ) … ( 1 − x N ) ( mod x N + 1 ) 需要
O ( N 2 ) O(N^2) O ( N 2 ) 或更差的复杂度,这太慢了。
幸运的是,欧拉发现了一个神奇的定理——
五边形数定理 :
∏ k = 1 ∞ ( 1 − x k ) = ∑ k = − ∞ ∞ ( − 1 ) k x g k = 1 + ∑ k = 1 ∞ ( − 1 ) k ( x g k + x g − k ) \prod_{k=1}^{\infty} (1-x^k) = \sum_{k=-\infty}^{\infty} (-1)^k x^{g_k} = 1 + \sum_{k=1}^{\infty} (-1)^k \left( x^{g_k} + x^{g_{-k}} \right) ∏ k = 1 ∞ ( 1 − x k ) = ∑ k = − ∞ ∞ ( − 1 ) k x g k = 1 + ∑ k = 1 ∞ ( − 1 ) k ( x g k + x g − k )
其中
g k = k ( 3 k − 1 ) 2 g_k = \frac{k(3k-1)}{2} g k = 2 k ( 3 k − 1 ) 是
广义五边形数 。
k = 0 k=0 k = 0 : g 0 = 0 g_0 = 0 g 0 = 0 , A ( x ) A(x) A ( x ) 有 + 1 ⋅ x 0 = 1 +1 \cdot x^0 = 1 + 1 ⋅ x 0 = 1
k = 1 k=1 k = 1 : g 1 = 1 g_1 = 1 g 1 = 1 , g − 1 = 2 g_{-1} = 2 g − 1 = 2 , A ( x ) A(x) A ( x ) 有 − 1 ⋅ x 1 − 1 ⋅ x 2 = − x − x 2 -1 \cdot x^1 - 1 \cdot x^2 = -x - x^2 − 1 ⋅ x 1 − 1 ⋅ x 2 = − x − x 2
k = 2 k=2 k = 2 : g 2 = 5 g_2 = 5 g 2 = 5 , g − 2 = 7 g_{-2} = 7 g − 2 = 7 , A ( x ) A(x) A ( x ) 有 + 1 ⋅ x 5 + 1 ⋅ x 7 = + x 5 + x 7 +1 \cdot x^5 + 1 \cdot x^7 = +x^5 + x^7 + 1 ⋅ x 5 + 1 ⋅ x 7 = + x 5 + x 7
k = 3 k=3 k = 3 : g 3 = 12 g_3 = 12 g 3 = 12 , g − 3 = 15 g_{-3} = 15 g − 3 = 15 , A ( x ) A(x) A ( x ) 有 − 1 ⋅ x 12 − 1 ⋅ x 15 -1 \cdot x^{12} - 1 \cdot x^{15} − 1 ⋅ x 12 − 1 ⋅ x 15
...
所以:
A ( x ) = 1 − x − x 2 + x 5 + x 7 − x 12 − x 15 + … A(x) = 1 - x - x^2 + x^5 + x^7 - x^{12} - x^{15} + \dots A ( x ) = 1 − x − x 2 + x 5 + x 7 − x 12 − x 15 + …
这是一个稀疏多项式 !
O ( N log N ) O(N \log N) O ( N log N ) 算法步骤
现在我们可以整合出完整的算法:
步骤 1:构造稀疏多项式 A ( x ) A(x) A ( x )
我们需要计算
A ( x ) = ∑ k = − ∞ ∞ ( − 1 ) k x g k ( m o d x N + 1 ) A(x) = \sum_{k=-\infty}^{\infty} (-1)^k x^{g_k} \pmod{x^{N+1}} A ( x ) = ∑ k = − ∞ ∞ ( − 1 ) k x g k ( mod x N + 1 ) 。
我们只需要计算所有
g k ≤ N g_k \le N g k ≤ N 的项。
由于
g k ≈ 3 k 2 2 g_k \approx \frac{3k^2}{2} g k ≈ 2 3 k 2 ,
k k k 的取值范围大致在
O ( N ) O(\sqrt{N}) O ( N ) 级别。
初始化一个全零的多项式(数组) A A A ,长度为 N + 1 N+1 N + 1 。
设置 A [ 0 ] = 1 A[0] = 1 A [ 0 ] = 1 。
从 k = 1 k = 1 k = 1 开始循环:
a. 计算五边形数 g k = k ( 3 k − 1 ) 2 g_k = \frac{k(3k-1)}{2} g k = 2 k ( 3 k − 1 ) 。
b. 如果 g k > N g_k > N g k > N ,跳出循环。
c. 根据 k k k 的奇偶性设置 A [ g k ] A[g_k] A [ g k ] :
* 如果 k k k 是奇数, A [ g k ] = − 1 A[g_k] = -1 A [ g k ] = − 1 。
* 如果 k k k 是偶数, A [ g k ] = + 1 A[g_k] = +1 A [ g k ] = + 1 。
d. 计算 g − k = k ( 3 k + 1 ) 2 g_{-k} = \frac{k(3k+1)}{2} g − k = 2 k ( 3 k + 1 ) 。
e. 如果 g − k > N g_{-k} > N g − k > N ,跳出循环(理论上可以只跳出这一半)。
f. 同样设置 A [ g − k ] A[g_{-k}] A [ g − k ] (符号与 g k g_k g k 相同)。
复杂度 :这个循环最多执行
O ( N ) O(\sqrt{N}) O ( N ) 次。所以构造
A ( x ) A(x) A ( x ) 的时间复杂度仅为
O ( N ) O(\sqrt{N}) O ( N ) 。
步骤 2:计算多项式求逆
我们现在有了
A ( x ) A(x) A ( x ) (一个
N + 1 N+1 N + 1 项的多项式,尽管它很稀疏)。
我们的目标是计算
P ( x ) P(x) P ( x ) 使得:
A ( x ) P ( x ) ≡ 1 ( m o d x N + 1 ) A(x) P(x) \equiv 1 \pmod{x^{N+1}} A ( x ) P ( x ) ≡ 1 ( mod x N + 1 )
这可以通过基于
牛顿迭代法 的标准多项式求逆算法实现,从而可以在
O ( N log N ) O(N \log N) O ( N log N ) 的时间内计算出
A ( x ) A(x) A ( x ) 的逆
P ( x ) P(x) P ( x ) 。
多项式求逆简介 :
假设我们已经求得
P 0 ( x ) P_0(x) P 0 ( x ) 满足
A ( x ) P 0 ( x ) ≡ 1 ( m o d x k ) A(x)P_0(x) \equiv 1 \pmod{x^k} A ( x ) P 0 ( x ) ≡ 1 ( mod x k ) 。
我们想求
P ( x ) P(x) P ( x ) 满足
A ( x ) P ( x ) ≡ 1 ( m o d x 2 k ) A(x)P(x) \equiv 1 \pmod{x^{2k}} A ( x ) P ( x ) ≡ 1 ( mod x 2 k ) 。
迭代公式为:
P ( x ) ≡ P 0 ( x ) ( 2 − A ( x ) P 0 ( x ) ) ( m o d x 2 k ) P(x) \equiv P_0(x) \left( 2 - A(x)P_0(x) \right) \pmod{x^{2k}} P ( x ) ≡ P 0 ( x ) ( 2 − A ( x ) P 0 ( x ) ) ( mod x 2 k )
从
P ( x ) ≡ 1 ( m o d x 1 ) P(x) \equiv 1 \pmod{x^1} P ( x ) ≡ 1 ( mod x 1 ) (即
p ( 0 ) = 1 p(0)=1 p ( 0 ) = 1 ) 开始,我们通过
log N \log N log N 次迭代(每次将精度加倍:
1 → 2 → 4 → ⋯ → N ′ 1 \to 2 \to 4 \to \dots \to N' 1 → 2 → 4 → ⋯ → N ′ ),每次迭代涉及两次多项式乘法(和一次减法),总复杂度为
O ( N log N ) O(N \log N) O ( N log N ) 。
步骤 3:获得答案
执行完多项式求逆后,得到的
P ( x ) = ∑ n = 0 N p ( n ) x n P(x) = \sum_{n=0}^{N} p(n)x^n P ( x ) = ∑ n = 0 N p ( n ) x n 。
数组
P P P 中的第
n n n 项
P [ n ] P[n] P [ n ] 即为分割函数
p ( n ) p(n) p ( n ) 的值。
总时间复杂度 :
O ( N + N log N ) = O ( N log N ) O(\sqrt{N} + N \log N) = O(N \log N) O ( N + N log N ) = O ( N log N ) 。
附:另一种 O ( N log N ) O(N \log N) O ( N log N ) 方法 (多项式 Exp)
顺便一提,还有一种相关的方法,不使用五边形数定理,而是使用多项式指数函数 。
P ( x ) = ∏ k = 1 ∞ 1 1 − x k P(x) = \prod_{k=1}^{\infty} \frac{1}{1-x^k} P ( x ) = ∏ k = 1 ∞ 1 − x k 1
取对数:ln P ( x ) = ∑ k = 1 ∞ − ln ( 1 − x k ) = ∑ k = 1 ∞ ∑ j = 1 ∞ ( x k ) j j = ∑ k = 1 ∞ ∑ j = 1 ∞ x k j j \ln P(x) = \sum_{k=1}^{\infty} -\ln(1-x^k) = \sum_{k=1}^{\infty} \sum_{j=1}^{\infty} \frac{(x^k)^j}{j} = \sum_{k=1}^{\infty} \sum_{j=1}^{\infty} \frac{x^{kj}}{j} ln P ( x ) = ∑ k = 1 ∞ − ln ( 1 − x k ) = ∑ k = 1 ∞ ∑ j = 1 ∞ j ( x k ) j = ∑ k = 1 ∞ ∑ j = 1 ∞ j x kj
交换求和次序,令 n = k j n = kj n = kj :
ln P ( x ) = ∑ n = 1 ∞ x n ( ∑ j ∣ n 1 j ) = ∑ n = 1 ∞ x n ( 1 n ∑ j ∣ n n j ) = ∑ n = 1 ∞ σ 1 ( n ) n x n \ln P(x) = \sum_{n=1}^{\infty} x^n \left( \sum_{j|n} \frac{1}{j} \right) = \sum_{n=1}^{\infty} x^n \left( \frac{1}{n} \sum_{j|n} \frac{n}{j} \right) = \sum_{n=1}^{\infty} \frac{\sigma_1(n)}{n} x^n ln P ( x ) = ∑ n = 1 ∞ x n ( ∑ j ∣ n j 1 ) = ∑ n = 1 ∞ x n ( n 1 ∑ j ∣ n j n ) = ∑ n = 1 ∞ n σ 1 ( n ) x n
(其中 σ 1 ( n ) \sigma_1(n) σ 1 ( n ) 是 n n n 的约数和)
令 Q ( x ) = ln P ( x ) Q(x) = \ln P(x) Q ( x ) = ln P ( x ) 。
算法 :
使用 O ( N log N ) O(N \log N) O ( N log N ) 的多项式指数函数 计算 P ( x ) = exp ( Q ( x ) ) ( m o d x N + 1 ) P(x) = \exp(Q(x)) \pmod{x^{N+1}} P ( x ) = exp ( Q ( x )) ( mod x N + 1 ) 。