五边形数定理
五边形数
定义如上图,通项公式为
p
n
=
3
n
2
−
n
2
p_n=\frac{3n^2-n}{2}
pn=23n2−n
广义五边形数
广义五边形数的公式和五边形数相同,只是
n
n
n 可以为负数和零,
n
n
n 依序为
0
,
1
,
−
1
,
2
,
−
2
,
3
,
−
3
,
4
⋯
0, 1, -1, 2, -2, 3, -3, 4\cdots
0,1,−1,2,−2,3,−3,4⋯ 广义五边形数也可以用下式表示:
p
n
=
2
n
2
±
n
2
p_n=\frac{2n^2\pm n}{2}
pn=22n2±n
n
n
n 依序为
0
,
1
,
2
,
3
,
4
⋯
0, 1, 2, 3, 4\cdots
0,1,2,3,4⋯
五边形数定理
欧拉函数的展开式如下:
∏
i
=
1
∞
(
1
−
x
i
)
=
∑
i
=
−
∞
∞
(
−
1
)
i
x
i
(
3
i
−
1
)
2
=
1
+
∑
i
=
1
∞
(
−
1
)
i
x
i
(
3
i
±
1
)
2
\prod_{i=1}^{\infty}(1-x^i)=\sum_{i=-\infty}^{\infty}(-1)^ix^{\frac{i(3i-1)}{2}}=1+\sum_{i=1}^\infty (-1)^ix^{\frac{i(3i\pm1)}{2}}
i=1∏∞(1−xi)=i=−∞∑∞(−1)ix2i(3i−1)=1+i=1∑∞(−1)ix2i(3i±1)
证明略。
欧拉函数展开后,有些次方项被消去,只留下次方项为 1 , 2 , 5 , 7 , 12 , ⋯ 1, 2, 5, 7, 12,\cdots 1,2,5,7,12,⋯ 的项次,留下来的次方恰为广义五边形数。
整数划分问题
使用正整数相加组合成n的方案数,比如 4 4 4 有以下几种: 1 + 1 + 1 + 1 , 2 + 2 , 4 , 1 + 3 , 1 + 1 + 2 1+1+1+1, 2+2,4,1+3,1+1+2 1+1+1+1,2+2,4,1+3,1+1+2。
-
dp 做法
设 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示使用不大于 j j j 的正整数来组成 i i i 的方案数,则状态转移方程为:
{ d p [ i ] [ j ] = d p [ i − j ] [ j ] + d p [ i ] [ j − 1 ] i ≥ j d p [ i ] [ j ] = d p [ i ] [ i ] i < j \left\{\begin{matrix} dp[i][j]=dp[i-j][j]+dp[i][j-1]&&i\geq j\\ dp[i][j]=dp[i][i]&& i<j \end{matrix}\right. {dp[i][j]=dp[i−j][j]+dp[i][j−1]dp[i][j]=dp[i][i]i≥ji<j -
生成函数+五边形数定理
设 n n n 的拆分方案数为 p ( n ) p(n) p(n) ,则欧拉函数的倒数为分割函数的母函数 F ( x ) F(x) F(x) ,即
F ( x ) = ∑ i = 0 ∞ p ( i ) x i = 1 ϕ ( x ) = ∏ i = 1 ∞ 1 1 − x i F(x)=\sum_{i=0}^\infty p(i)x^i=\frac{1}{\phi(x)}=\prod_{i=1}^\infty\frac{1}{1-x^i} F(x)=i=0∑∞p(i)xi=ϕ(x)1=i=1∏∞1−xi1
证明:对于每一个数 i i i 考虑选 0 , 1 , 2 , 3 , ⋯ 0,1,2,3,\cdots 0,1,2,3,⋯ 次,则生成函数为
g ( i ) = 1 + x i + x 2 i + ⋯ = 1 1 − x i g(i)=1+x^i+x^{2i}+\cdots=\frac{1}{1-x^i} g(i)=1+xi+x2i+⋯=1−xi1
将每一个数的生成函数相乘,得:
F ( x ) = ∏ i = 0 ∞ g ( i ) = 1 ϕ ( x ) F(x)=\prod_{i=0}^\infty g(i)=\frac{1}{\phi(x)} F(x)=i=0∏∞g(i)=ϕ(x)1
得证。由于上述等式成立,考虑等式两边同乘以 ϕ ( x ) \phi(x) ϕ(x) ,得
F ( x ) ϕ ( x ) = 1 F(x)\phi(x)=1 F(x)ϕ(x)=1
根据五边形数定理展开得
∑ i = 0 ∞ p ( i ) x i ( 1 + ∑ j = 1 ∞ ( − 1 ) j x j ( 3 j ± 1 ) 2 ) = 1 \sum_{i=0}^\infty p(i)x^i(1+\sum_{j=1}^\infty (-1)^jx^{\frac{j(3j\pm1)}{2}})=1 i=0∑∞p(i)xi(1+j=1∑∞(−1)jx2j(3j±1))=1
由于等式右边不含 x x x ,因此等式左边展开后 x k x^k xk 的系数均为 0 0 0 。因此由五边形数定理展开后 x x x 的幂次为广义五边形数,通过固定展开式中 x x x 的幂次可以得出如下递推式:
p ( n ) = p ( n − 1 ) + p ( n − 2 ) − p ( n − 5 ) − p ( n − 7 ) + ⋯ p(n)=p(n-1)+p(n-2)-p(n-5)-p(n-7)+\cdots p(n)=p(n−1)+p(n−2)−p(n−5)−p(n−7)+⋯
而上式中 n n n 减掉的数正是广义五边形数。由于五边形数的增长速度是 O ( n 2 ) O(n^2) O(n2) ,因此可以 O ( n ) O(\sqrt n) O(n) 枚举五边形数暴力 dp ,总时间复杂度为 O ( n n ) O(n\sqrt n) O(nn) 。
求解 p ( n ) p(n) p(n) ,方法如上。
const int N = 1e5 + 10, MOD = 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= MOD) x -= MOD;
}
inline void sub(int &x, int y) {
x -= y;
if (x < 0) x += MOD;
}
int p[600], T, dp[N], n[N], mxn;
int main() {
scanf("%d", &T);
for (int i = 1; i <= T; i++)scanf("%d", &n[i]);
mxn = *max_element(n + 1, n + 1 + T);
for (int i = 1, j = 1;; i += 2, j++) {
p[i] = (3 * j * j - j) >> 1;
if (p[i] > mxn)break;
p[i + 1] = (3 * j * j + j) >> 1;
if (p[i + 1] > mxn)break;
}
dp[0] = 1;
for (int i = 1; i <= mxn; i++)
for (int j = 1; p[j] <= i; j++) {
if ((j + 1) >> 1 & 1) add(dp[i], dp[i - p[j]]);
else sub(dp[i], dp[i - p[j]]);
}
for (int i = 1; i <= T; i++)
printf("%d\n", dp[n[i]]);
return 0;
}
将一个正整数 n n n 划分为多个正整数的和的方案数,但每个数的使用次数小于 k k k 次。
不难得出生成函数为:
G
(
x
)
=
∏
i
=
1
∞
∑
j
=
0
k
−
1
x
i
j
G(x)=\prod_{i=1}^{\infty}\sum_{j=0}^{k-1}x^{ij}
G(x)=i=1∏∞j=0∑k−1xij
显然内部求和部分可以根据等比数列求和化简,得
G
(
x
)
=
∏
i
=
1
∞
1
−
x
k
i
1
−
x
i
=
ϕ
(
x
k
)
ϕ
(
x
)
G(x)=\prod_{i=1}^{\infty}\frac{1-x^{ki}}{1-x^i}=\frac{\phi(x^k)}{\phi(x)}
G(x)=i=1∏∞1−xi1−xki=ϕ(x)ϕ(xk)
此时如果按两边同乘以
ϕ
(
x
)
\phi(x)
ϕ(x) ,然后暴力展开找递推式的方法做就会发现等式右边
ϕ
(
x
k
)
\phi(x^k)
ϕ(xk) 中的
x
i
x^i
xi 系数不固定,求解相当麻烦,因此考虑转化到已求解的问题上去。
观察发现:
G
(
x
)
=
ϕ
(
x
k
)
F
(
x
)
=
(
1
+
∑
i
=
1
∞
(
−
1
)
i
x
k
i
(
3
i
±
1
)
2
)
(
∑
i
=
0
∞
p
(
i
)
x
i
)
G(x)=\phi(x^k)F(x)\\ =(1+\sum_{i=1}^\infty (-1)^ix^{k\frac{i(3i\pm1)}{2}})(\sum_{i=0}^\infty p(i)x^i)
G(x)=ϕ(xk)F(x)=(1+i=1∑∞(−1)ixk2i(3i±1))(i=0∑∞p(i)xi)
设
G
(
x
)
=
∑
i
=
0
∞
p
′
(
i
)
x
i
G(x)=\sum_{i=0}^\infty p'(i)x^i
G(x)=i=0∑∞p′(i)xi
则
p
′
(
n
)
=
p
(
n
)
−
p
(
n
−
k
)
−
p
(
n
−
2
k
)
+
p
(
n
−
5
k
)
+
⋯
p'(n)=p(n)-p(n-k)-p(n-2k)+p(n-5k)+\cdots
p′(n)=p(n)−p(n−k)−p(n−2k)+p(n−5k)+⋯
其中
p
(
1
∼
n
)
p(1\sim n)
p(1∼n) 可以
O
(
n
n
)
O(n\sqrt n)
O(nn) 预处理,之后每个
p
′
(
n
)
p'(n)
p′(n) 均可以
O
(
n
)
O(\sqrt n)
O(n) 查询。时间复杂度为
O
(
(
n
+
T
)
n
)
O((n+T)\sqrt n)
O((n+T)n) 。
const int N = 1e5 + 10, MOD = 1e9 + 7;
inline void add(int &x, int y) {
x += y;
if (x >= MOD) x -= MOD;
}
inline void sub(int &x, int y) {
x -= y;
if (x < 0) x += MOD;
}
int p[600], T, dp[N], n[N], k[N], mxn;
int main() {
scanf("%d", &T);
for (int i = 1; i <= T; i++)
scanf("%d%d", &n[i], &k[i]);
mxn = *max_element(n + 1, n + 1 + T);
for (int i = 1, j = 1;; i += 2, j++) {
p[i] = (3 * j * j - j) >> 1;
if (p[i] > mxn)break;
p[i + 1] = (3 * j * j + j) >> 1;
if (p[i + 1] > mxn)break;
}
dp[0] = 1;
for (int i = 1; i <= mxn; i++)
for (int j = 1; p[j] <= i; j++) {
if ((j + 1) >> 1 & 1) add(dp[i], dp[i - p[j]]);
else sub(dp[i], dp[i - p[j]]);
}
for (int i = 1; i <= T; i++) {
int ans = dp[n[i]];
for (int j = 1; 1ll * k[i] * p[j] <= n[i]; j++) {
if ((j + 1) >> 1 & 1)sub(ans, dp[n[i] - k[i] * p[j]]);
else add(ans, dp[n[i] - k[i] * p[j]]);
}
printf("%d\n", ans);
}
return 0;
}