本文用到生成函数,这里有篇我觉得讲的不错的添加链接描述
这题的意思是问一个整数可以拆分为多少种整数的相加,设数目为
P
(
n
)
P(n)
P(n)
因为
5
=
1
+
1
+
1
+
1
+
1
5=1+1+1+1+1
5=1+1+1+1+1
5
=
1
+
1
+
1
+
2
5=1+1+1+2
5=1+1+1+2
5
=
1
+
1
+
3
5=1+1+3
5=1+1+3
5
=
1
+
4
5=1+4
5=1+4
5
=
2
+
3
5=2+3
5=2+3
5
=
1
+
2
+
2
5=1+2+2
5=1+2+2
5
=
5
5=5
5=5
所以
P
(
5
)
=
7
P(5)=7
P(5)=7
有一些比较低效的方法,这里不说,说一种时间复杂度为
O
(
n
n
)
O(n\sqrt{n})
O(nn)的解法。
先说一下五边形数定理
五边形数定理
五边形数,即是如图所示,组成正五边形数的点数
这里贴一个来自百度百科的图片
第几个五边形数即是图片中对应第几张中的点数是多少
即是
1
,
5
,
12
,
22
,
35
,
51
⋯
1, 5, 12, 22, 35, 51\cdots
1,5,12,22,35,51⋯
设
x
x
x个五边形数为
K
(
x
)
K(x)
K(x)
观察图片可发现
K
(
x
)
=
K
(
x
−
1
)
+
3
∗
x
−
2
K(x)=K(x-1)+3*x-2
K(x)=K(x−1)+3∗x−2
K
(
x
)
=
1
+
4
+
7
+
10
+
13
+
⋯
3
∗
x
−
2
=
x
⋅
(
3
x
−
1
)
2
K(x)=1+4+7+10+13+\cdots 3*x-2=\frac{x\cdot(3x-1)}{2}
K(x)=1+4+7+10+13+⋯3∗x−2=2x⋅(3x−1)
用这公式推广到广义五角形定理。
x
x
x的取值是
0
,
1
,
1
,
2
,
−
2
,
3
,
−
3
⋯
0,1,1,2,-2, 3,-3\cdots
0,1,1,2,−2,3,−3⋯
所以五边形数是
0
,
1
,
2
,
5
,
7
,
12
,
15
,
22
,
26
,
35
,
40
,
51
,
57
,
70
,
77
,
92
⋯
0, 1, 2, 5, 7, 12, 15, 22, 26, 35, 40, 51, 57, 70, 77, 92\cdots
0,1,2,5,7,12,15,22,26,35,40,51,57,70,77,92⋯
五边形数定理是一个由欧拉发现的数学定理,描述欧拉函数展开式的特性。–百度百科
定理描述:–百度百科
即
这个公式可看到指数部分即是广义五边形数,为第
0
,
1
−
1
,
2
,
−
2
⋯
0,1-1,2,-2\cdots
0,1−1,2,−2⋯个,前面系数为
(
−
1
)
∣
m
∣
(-1)^{|m|}
(−1)∣m∣
m
m
m表示第
m
m
m个五边形数
为什么是这样我也不知道,乘开后就是这样了。
那接下来描述这个定理跟我们的拆分整数有什么关系
为防止重复,我们可以这样描述一个整数n可以拆分为多少个1,多少个2,多少个3等等组成
即
n
=
1
∗
k
1
+
2
∗
k
2
+
3
∗
k
3
+
⋯
+
n
∗
k
n
n=1*k_1+2*k_2+3*k_3+\cdots+n*k_n
n=1∗k1+2∗k2+3∗k3+⋯+n∗kn
k
=
0
,
1
,
⋯
k=0,1,\cdots
k=0,1,⋯
我们把多少个1,多少个2,多少个3,搬到指数上去
所以易知
∏
i
=
1
∞
∑
j
=
0
∞
x
i
j
=
(
1
+
x
1
+
x
2
+
x
3
+
⋯
)
(
1
+
x
1
∗
2
+
x
2
∗
2
+
x
3
∗
2
+
⋯
)
(
1
+
x
1
∗
3
+
x
2
∗
3
+
x
3
∗
3
⋯
)
⋯
=
∏
i
=
0
∞
P
(
i
)
x
i
\prod\limits _{i=1}^{\infty}\sum\limits_{j=0}^{\infty}x^{ij}= (1+x^1+x^2+x^3+\cdots)(1+x^{1*2}+x^{2*2}+x^{3*2}+\cdots)(1+x^{1*3}+x^{2*3}+x^{3*3}\cdots)\cdots=\prod\limits_{i=0}^{\infty}P(i)x^i
i=1∏∞j=0∑∞xij=(1+x1+x2+x3+⋯)(1+x1∗2+x2∗2+x3∗2+⋯)(1+x1∗3+x2∗3+x3∗3⋯)⋯=i=0∏∞P(i)xi
这里不妨尝试这选定特定的
i
i
i,即
x
i
x^i
xi尝试着拆一下,可以发现就是右式展开后指数相加的形式了
由生成函数
1
1
−
x
=
1
+
x
+
x
2
+
x
3
+
x
4
+
⋯
\frac{1}{1-x}=1+x+x^2+x^3+x^4+\cdots
1−x1=1+x+x2+x3+x4+⋯得
∏
i
=
0
∞
P
(
i
)
x
i
=
∏
i
=
1
∞
1
1
−
x
i
\prod\limits_{i=0}^{\infty}P(i)x^i=\prod\limits_{i=1}^{\infty}\frac{1}{1-x^i}
i=0∏∞P(i)xi=i=1∏∞1−xi1这就是把五边形定理跟整数拆分结合在一起的巧妙公式了
设
Q
(
x
)
=
∏
i
=
1
∞
(
1
−
x
i
)
Q(x)=\prod\limits_{i=1}^{\infty}(1-x^i)
Q(x)=i=1∏∞(1−xi)
则结合五边形数定理有
Q
(
x
)
∏
i
=
0
∞
P
(
i
)
x
i
=
(
P
(
0
)
+
P
(
1
)
x
+
P
(
2
)
x
2
+
P
(
3
)
x
3
+
⋯
)
(
1
−
x
−
x
2
+
x
5
+
x
7
−
x
12
⋯
)
=
1
Q(x)\prod\limits_{i=0}^{\infty}P(i)x^i=(P(0)+P(1)x+P(2)x^2+P(3)x^3+\cdots)(1-x-x^2+x^5+x^7-x^{12}\cdots)=1
Q(x)i=0∏∞P(i)xi=(P(0)+P(1)x+P(2)x2+P(3)x3+⋯)(1−x−x2+x5+x7−x12⋯)=1
P(0)=1;
显然左边展开之后
x
k
x^k
xk前的系数为0
所以
P
(
k
)
=
P
(
k
−
1
)
+
P
(
k
−
2
)
−
P
(
k
−
5
)
−
P
(
k
−
7
)
+
P
(
k
−
12
)
+
P
(
k
−
15
)
P(k)=P(k-1)+P(k-2)-P(k-5)-P(k-7)+P(k-12)+P(k-15)
P(k)=P(k−1)+P(k−2)−P(k−5)−P(k−7)+P(k−12)+P(k−15)
即是广义五边形数,为第
0
,
1
−
1
,
2
,
−
2
⋯
0,1-1,2,-2\cdots
0,1−1,2,−2⋯个,前面系数为
(
−
1
)
∣
m
∣
+
1
(-1)^{|m|+1}
(−1)∣m∣+1
而
K
(
x
)
=
K
(
x
−
1
)
+
3
∗
x
−
2
K(x)=K(x-1)+3*x-2
K(x)=K(x−1)+3∗x−2
至持这题的整数拆分完美解决,预处理前
n
个
P
n个P
n个P时间复杂度为
O
(
n
n
)
O(n\sqrt{n})
O(nn)
HDU 4651代码
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=4e5+7;
const int mod=1e9+7;
using namespace std;
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
int top=0;
map<pair<int,int>,int>mp;
ll a[MX],P[MX];
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int t=1e5;
for(int i=-t;i<=t;i++)
{
a[i+t]=1ll*i*(3*i-1)/2;
}//存第i个五边形数
P[1]=1,P[0]=1;
for(int i=2;i<=t;i++)
{
for(int j=1;;j++)
{
if(a[j+t]<=i)
{
if(j&1)
P[i]+=P[i-a[j+t]];
else P[i]-=P[i-a[j+t]];//根据递归公式
}
if(a[t-j]<=i)
{
if(j&1)
{
P[i]+=P[i-a[t-j]];
}
else P[i]-=P[i-a[t-j]];
}
if(a[j+t]>i&&a[t-j]>i)break;
P[i]%=mod;
if(P[i]<0)P[i]+=mod;
}
}
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
cout<<P[n]<<endl;
}
}
HDU4658
这题较HDU4651多了一个限制,整数拆分后每个数的个数不能等于或多于k个,即不能有k个1,或超过k个1
设对于
n
,
k
n,k
n,k答案为
P
k
(
n
)
P_k(n)
Pk(n)
同样构造一下有
∏
i
=
1
∞
∑
j
=
0
k
−
1
x
i
j
=
(
1
+
x
1
+
x
2
+
x
3
+
⋯
+
x
k
−
1
)
(
1
+
x
1
∗
2
+
x
2
∗
2
+
x
3
∗
2
+
⋯
+
x
(
k
−
1
)
2
)
(
1
+
x
1
∗
3
+
x
2
∗
3
+
x
3
∗
3
⋯
+
x
(
k
−
1
)
3
)
⋯
=
∏
i
=
0
∞
P
k
(
i
)
x
i
\prod\limits _{i=1}^{\infty}\sum\limits_{j=0}^{k-1}x^{ij}= (1+x^1+x^2+x^3+\cdots+x^{k-1})(1+x^{1*2}+x^{2*2}+x^{3*2}+\cdots+x^{(k-1)2})(1+x^{1*3}+x^{2*3}+x^{3*3}\cdots+x^{(k-1)3})\cdots=\prod\limits_{i=0}^{\infty}P_k(i)x^i
i=1∏∞j=0∑k−1xij=(1+x1+x2+x3+⋯+xk−1)(1+x1∗2+x2∗2+x3∗2+⋯+x(k−1)2)(1+x1∗3+x2∗3+x3∗3⋯+x(k−1)3)⋯=i=0∏∞Pk(i)xi
由等比数列前n项和易知
∏
i
=
0
∞
P
k
(
i
)
x
i
=
∏
i
=
1
∞
1
−
x
k
i
1
−
x
i
=
Q
(
x
k
)
Q
(
x
)
=
Q
(
x
k
)
∏
i
=
0
∞
P
(
i
)
x
i
\prod\limits_{i=0}^{\infty}P_k(i)x^i=\prod\limits_{i=1}^{\infty}\frac{1-x^{ki}}{1-x^i}=\frac{Q(x^{k})}{Q(x)}=Q(x^k)\prod\limits_{i=0}^{\infty}P(i)x^i
i=0∏∞Pk(i)xi=i=1∏∞1−xi1−xki=Q(x)Q(xk)=Q(xk)i=0∏∞P(i)xi
对
Q
(
x
k
)
Q(x^k)
Q(xk)展开有
Q
(
x
k
)
=
(
1
−
x
k
−
x
2
k
+
x
5
k
+
x
7
k
−
x
12
k
⋯
)
Q(x^k)=(1-x^k-x^{2k}+x^{5k}+x^{7k}-x^{12k}\cdots)
Q(xk)=(1−xk−x2k+x5k+x7k−x12k⋯)
所以
(
P
k
(
0
)
+
P
k
(
1
)
x
1
+
P
k
(
2
)
x
2
+
⋯
)
=
(
1
−
x
k
−
x
2
k
+
x
5
k
+
x
7
k
−
x
12
k
⋯
)
(
P
(
0
)
+
P
(
1
)
x
+
P
(
2
)
x
2
+
P
(
3
)
x
3
+
⋯
)
(P_k(0)+P_k(1)x^1+P_k(2)x^2+\cdots)=(1-x^k-x^{2k}+x^{5k}+x^{7k}-x^{12k}\cdots)(P(0)+P(1)x+P(2)x^2+P(3)x^3+\cdots)
(Pk(0)+Pk(1)x1+Pk(2)x2+⋯)=(1−xk−x2k+x5k+x7k−x12k⋯)(P(0)+P(1)x+P(2)x2+P(3)x3+⋯)
整理出来有
P
k
(
n
)
=
P
(
n
)
−
P
(
n
−
1
)
−
P
(
n
−
2
)
+
P
(
n
−
5
)
+
P
(
n
−
7
)
⋯
P_k(n)=P(n)-P(n-1)-P(n-2)+P(n-5)+P(n-7)\cdots
Pk(n)=P(n)−P(n−1)−P(n−2)+P(n−5)+P(n−7)⋯
先预处理一下P的值,然后就可以做了
代码:
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int MX=1e6+7;
const int mod=1e9+7;
using namespace std;
ll qpow(ll a,ll b,ll MOD=mod){for(ll ans=1;;a=a*a%MOD,b>>=1){if(b&1)ans=ans*a%MOD;if(!b)return ans;}}
ll inv(ll a,ll MOD=mod){return qpow(a,MOD-2,MOD);}
ll __gcm(ll a,ll b){return a*b/__gcd(a,b);}
int top=0,t;
map<pair<int,int>,int>mp;
ll a[MX],P[MX];
void Partition()
{
t=1e5;
for(int i=-t;i<=t;i++)
{
a[i+t]=1ll*i*(3*i-1)/2;
}
P[1]=1,P[0]=1;
for(int i=2;i<=t;i++)
{
for(int j=1;;j++)
{
if(a[j+t]<=i)
{
if(j&1)P[i]+=P[i-a[j+t]];
else P[i]-=P[i-a[j+t]];
}
if(a[t-j]<=i)
{
if(j&1)P[i]+=P[i-a[t-j]];
else P[i]-=P[i-a[t-j]];
}
if(a[j+t]>i&&a[t-j]>i)break;
P[i]%=mod;
if(P[i]<0)P[i]+=mod;
}
}
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0);
int T;
Partition();
cin>>T;
while(T--)
{
int n,k;
cin>>n>>k;
ll ans=P[n];
for(int j=1;;j++)
{
if(k*a[j+t]<=n)
{
if(j&1)ans-=P[n-k*a[j+t]];
else ans+=P[n-k*a[j+t]];
}
if(k*a[t-j]<=n)
{
if(j&1)ans-=P[n-k*a[t-j]];
else ans+=P[n-k*a[t-j]];
}
if(a[j+t]>n&&a[t-j]>n)break;
ans%=mod;
if(ans<0)ans+=mod;
}
cout<<ans<<endl;
}
}