知识点 - 分拆数/整数拆分
解决问题类型:
将一个数用一个或多个正整数的无序和来表示有几种方案。
这是一个母函数的应用,利用了母函数的指数系数是五边形数来优化复杂度。
结论
(1)称正整数n分解为r个正整数和的个数为n分解成r的分拆数,记为 P r ( n ) P_{r}\left( n \right) Pr(n)
(2) P 1 ( n ) P_{1}\left( n \right) P1(n)=1; P n ( n ) P_{n}\left( n \right) Pn(n)=1; P n − 1 ( n ) P_{n - 1}\left( n \right) Pn−1(n)=1; P n − 2 ( n ) P_{n - 2}\left( n \right) Pn−2(n)=2; P n − 3 ( n ) P_{n - 3}\left( n \right) Pn−3(n)=3
(3) P 2 ( n ) = ⌈ n − 1 2 ⌉ , n ≥ 2 P_{2}\left( n \right) = \left\lceil \frac{n - 1}{2} \right\rceil,n \geq 2 P2(n)=⌈2n−1⌉,n≥2
(4) P r ( n ) = P 1 ( n − r ) + P 2 ( n − r ) + ⋅ ⋅ ⋅ + P r ( n − r ) P_{r}\left( n \right) = P_{1}\left( n - r \right) + P_{2}\left( n - r \right) + \cdot \cdot \cdot + P_{r}\left( n - r \right) Pr(n)=P1(n−r)+P2(n−r)+⋅⋅⋅+Pr(n−r)
性质
- ·n的分拆数中最大部分为m的个数=把n分拆成m部分的个数
- ·n的分拆数中每一部分都小于等于m的个数=把n分成m份或更小
- ·n的分拆数中每部分的数都相等的个数=n 的因子个数
- ·n的分拆数中每部分都是1或2(或者把n分拆成1或2部分)的个数 = f l o o r ( n / 2 + 1 ) =floor(n/2+1) =floor(n/2+1);
- ·n的分拆数中每部分都是1或2或3(或者把n分拆成1或2或3部分)的个数 = ( n + 3 ) 2 / 12 =(n+3)^2/12 =(n+3)2/12;
复杂度:
O ( n n ) O(n\sqrt{n}) O(nn)?
例题
若令划分函数P(n)为答案,则有
P
(
n
)
=
Σ
(
−
1
)
(
i
−
1
)
P
(
n
−
q
i
)
(
q
i
<
=
n
)
P(n) = \Sigma(-1)^{(i-1)}P(n-q_i) (q_i <= n)
P(n)=Σ(−1)(i−1)P(n−qi)(qi<=n)
其中q为五边形数:
q
i
=
3
i
(
i
−
1
)
/
2
q_i=3i(i-1)/2
qi=3i(i−1)/2
证明(母函数):
将一个正整数N拆成不少于一个数的和,且每个数的出现次数不超过K次
exd:
BZOJ4772 显而易见的数论
Σ
划分方案
p
Σ
i
Σ
j
>
i
g
(
a
F
(
p
i
,
p
j
)
m
o
d
k
)
\Sigma _{划分方案p}\Sigma_i\Sigma_{j>i}g(a_F(p_i,p_j)modk)
Σ划分方案pΣiΣj>ig(aF(pi,pj)modk)
代码
//T1, https://www.cnblogs.com/geloutingyu/p/7599415.html
#include <iostream>
using namespace std;
const int mod = 1e9 + 7;
const int MAXN = 1e5 + 1;
int f[MAXN];
void get_f(void){
f[0] = 1;
for(int i = 1; i < MAXN; i++){
for(int j = 1, cnt = 1; i - (3 * j * j - j) / 2 >= 0; j++, cnt *= -1){
int cc = 3 * j * j;
f[i] += f[i - (cc - j) / 2] * cnt;
f[i] %= mod;
f[i] = (f[i] + mod) % mod;
if(i >= (cc + j) / 2){
f[i] += f[i - (cc + j) / 2] * cnt;
f[i] %= mod;
f[i] = (f[i] + mod) % mod;
}
}
}
}
int main(void){
get_f();
int t, x;
cin >> t;
while(t--){
cin >> x;
cout << f[x] << endl;
}
return 0;
}
//T2 T1基础上加个限制 k
// https://www.cnblogs.com/geloutingyu/p/7603378.html
#include <iostream>
#include <stdio.h>
using namespace std;
const int mod = 1e9 + 7;
const int MAXN = 1e5 + 1;
int f[MAXN];
void get_f(void){
f[0] = 1;
for(int i = 1; i < MAXN; i++){
for(int j = 1, cnt = 1; i - (3 * j * j - j) / 2 >= 0; j++, cnt *= -1){
int cc = 3 * j * j;
f[i] += f[i - (cc - j) / 2] * cnt;
f[i] %= mod;
f[i] = (f[i] + mod) % mod;
if(i >= (cc + j) / 2){
f[i] += f[i - (cc + j) / 2] * cnt;
f[i] %= mod;
f[i] = (f[i] + mod) % mod;
}
}
}
}
int solve(int n, int k){
int ans = f[n];
for(int i = 1, cnt = -1; n - k * (3 * i * i - i) / 2 >= 0; i++, cnt *= -1){
ans += f[n - k * (3 * i * i - i) / 2] * cnt;
ans %= mod;
ans = (ans + mod) % mod;
if(n - k * (3 * i * i + i) / 2 >= 0){
ans += f[n - k * (3 * i * i + i) / 2] * cnt;
ans %= mod;
ans = (ans + mod) % mod;
}
}
return ans;
}
int main(void){
get_f();
int t, x, k;
scanf("%d", &t);
while(t--){
scanf("%d%d", &x, &k);
printf("%d\n", solve(x, k));
}
return 0;
}