本文用到生成函数,这里有篇我觉得讲的不错的添加链接描述
这题的意思是问一个整数可以拆分为多少种整数的相加,设数目为 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
#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
#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;
}
}