HDU 4651Partition&HDU4658(浅析整数拆分,五边形数定理)

本文用到生成函数,这里有篇我觉得讲的不错的添加链接描述
这题的意思是问一个整数可以拆分为多少种整数的相加,设数目为 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(x1)+3x2
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+3x2=2x(3x1)
用这公式推广到广义五角形定理。
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,11,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=1k1+2k2+3k3++nkn
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=1j=0xij=1+x1+x2+x3+(1+x12+x22+x32+)(1+x13+x23+x33)=i=0P(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 1x1=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=0P(i)xi=i=11xi1这就是把五边形定理跟整数拆分结合在一起的巧妙公式了
Q ( x ) = ∏ i = 1 ∞ ( 1 − x i ) Q(x)=\prod\limits_{i=1}^{\infty}(1-x^i) Q(x)=i=1(1xi)
则结合五边形数定理有
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=0P(i)xi=(P(0)+P(1)x+P(2)x2+P(3)x3+)(1xx2+x5+x7x12)=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(k1)+P(k2)P(k5)P(k7)+P(k12)+P(k15)
即是广义五边形数,为第 0 , 1 − 1 , 2 , − 2 ⋯ 0,1-1,2,-2\cdots 0,11,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(x1)+3x2
至持这题的整数拆分完美解决,预处理前 n 个 P n个P nP时间复杂度为 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=1j=0k1xij=1+x1+x2+x3++xk1(1+x12+x22+x32++x(k1)2)(1+x13+x23+x33+x(k1)3)=i=0Pk(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=0Pk(i)xi=i=11xi1xki=Q(x)Q(xk)=Q(xk)i=0P(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)=(1xkx2k+x5k+x7kx12k)
所以
( 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+)=(1xkx2k+x5k+x7kx12k)(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(n1)P(n2)+P(n5)+P(n7)
先预处理一下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;
  }

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值