本文用到生成函数,这里有篇我觉得讲的不错的添加链接描述
这题的意思是问一个整数可以拆分为多少种整数的相加,设数目为
因为
所以
有一些比较低效的方法,这里不说,说一种时间复杂度为
的解法。
先说一下五边形数定理
五边形数定理
五边形数,即是如图所示,组成正五边形数的点数
这里贴一个来自百度百科的图片
第几个五边形数即是图片中对应第几张中的点数是多少
即是
设
个五边形数为
观察图片可发现
用这公式推广到广义五角形定理。
的取值是
所以五边形数是
五边形数定理是一个由欧拉发现的数学定理,描述欧拉函数展开式的特性。–百度百科
定理描述:–百度百科
即
这个公式可看到指数部分即是广义五边形数,为第
个,前面系数为
表示第
个五边形数
为什么是这样我也不知道,乘开后就是这样了。
那接下来描述这个定理跟我们的拆分整数有什么关系
为防止重复,我们可以这样描述一个整数n可以拆分为多少个1,多少个2,多少个3等等组成
即
我们把多少个1,多少个2,多少个3,搬到指数上去
所以易知
这里不妨尝试这选定特定的
,即
尝试着拆一下,可以发现就是右式展开后指数相加的形式了
由生成函数
得
这就是把五边形定理跟整数拆分结合在一起的巧妙公式了
设
则结合五边形数定理有
P(0)=1;
显然左边展开之后
前的系数为0
所以
即是广义五边形数,为第
个,前面系数为
而
至持这题的整数拆分完美解决,预处理前
时间复杂度为
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项和易知
对
展开有
所以
整理出来有
先预处理一下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;
}
}