分拆数 && hdu 4651 && hdu 4658

转载自:http://blog.csdn.net/zhoufenqin/article/details/9821617

分拆数

在将分拆数之前先介绍一点五边形数

http://en.wikipedia.org/wiki/Pentagonal_number 


1. 五边形数是能排成五边形多边形数。 

第n个五边形数公式:p(n)=(3*n^2-n)/2

前几个五边形数:15122235517092117, 145, 176, 210, 247, 287, 330, 376, 425, 477, 532, 590, 651, 715, 782, 852, 925, 1001 .........

2. 广义五边形数:

n的取值0,1,-1,2,-2,3,-3.......

前几个广义五边形数:0, 1, 2, 5, 7, 12, 15, 22, 26, 35, 40, 51, 57, 70, 77, 92, 100, 117, 126, 145, 155, 176, 187, 210, 222, 247, 260, 287, 301, 330......

3. 中心五边形数:


4.中心六边形数

中心六边形数跟广义五边形数有较大的关系,见图



(相邻两个广义五边形数之和)

5. 五边形数测试

利用以下的公式可以测试一个正整数x是否是五边形数(此处不考虑广义五边形数):


n自然数,则x是五边形数,而且恰为第n个五边形数。

n不是自然数,则x不是五边形数。

进入正题:分拆数

将一个数用一个或多个正整数的无序和来表示

例如4的分拆有5种:4 , 3+1 , 2+2 , 2+1+1 , 1+1+1+1

1. 限制分拆

给一些分拆加限制条件。例如8的分拆有22种,

其中分拆的数中全部都是奇数的有6种:7+1, 5+3, 5+1+1+1, 3+3+1+1, 3+1+1+1+1+1, 1+1+1+1+1+1+1+1;

同样,若要求8分拆的数中是两两不同的也有6种:8, 7+1, 6+2, 5+3, 5+2+1, 4+3+1

已证明一个数的分拆中满足以上两种条件的个数是相同的,详见http://en.wikipedia.org/wiki/Glaisher%27s_theorem 

一些有关限制分拆的结论:

·n的分拆数中最大部分为m的个数=n分拆成m部分的个数


如图,左边最大部分m=3,等于把n拆成3部分(右图)   把图转置即可

·n的分拆数中每一部分都小于等于m的个数=n分成m份或更小

·n的分拆数中每部分的数都相等的个数=n 的因子个数

Eg. 6=2+2+2, 6=3+3,6=1+1+1+1+1+1

·n的分拆数中每部分都是12(或者把n分拆成12部分)的个数=floor(n/2+1);

 Eg. 6=1+1+1+1+1+1, 6=1+1+1+1+2, 6=1+1+2+2, 6=2+2+2

·n的分拆数中每部分都是123(或者把n分拆成123部分)的个数=(n+3)^2/12;

2. 生成函数

     

因为 ,右边的表达式等于乘积(母函数)

Pn 等于方程n=a1+2*a2+3*a3+...+n*an 的非负整数解a1,a2...an 的个数。

若是用母函数的方法去做,n很大不容易解出来,继续往下看

定义P(k,n)为:将n表示成>=k 的数之和。Eg P(3,6): 6=3+3 

1) 当最小的数为k时,p(k,n) = p(k,n-k)。即在n-k表示成>=k的数之和的情况下再加入一个k,情况还是不变

2) 当最小的数>k时,则至少最小的数为k+1p(k,n)=p(k+1,n);

所以p(k,n)=p(k,n-k)+p(k+1,n)k>n 时,p(k,n)=0,p(n,n)=1;

  eg: p(9)=p(1,8)+p(2,7)+p(3,6)+p(4,5);

得到下表:

 K

n

1

2

3

4

5

6

7

...

1

1

2

2

1

3

3

1

1

4

5

2

1

1

5

7

2

1

1

1

6

11

4

2

1

1

1

7

15

4

2

1

1

1

1

...

前几个分拆数1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42 ,56,77,101,135......

如果是按p(k,n)=p(k+1,n)+p(k,n-k) 来算,复杂度仍是0(n^2),往下看

 

右边的分母是欧拉函数,可写成

 可以发现等式右边x的指数为扩展五边形数,可根据之前介绍的扩展五边形公式算得p(n)=(3*n^2-n)/2,系数符号为(-1)^(m+1)

于是 p(k) = p(k − 1) + p(k − 2) − p(k − 5) − p(k − 7) + p(k − 12) + p(k − 15)  − p(k − 22) − ... 

Eg p(10)=p(9)+p(8)-p(5)-p(3)。复杂度降低了

/*

总结:

五边形数:0, 1, 2, 5, 7, 12, 15, 22, 26, 35....

对应下标:0, 1, -1, 2, -2, 3, -3, 4, -4 , 5.....



所以 可以在O(N^1.5)的时间内求出p(1),p(2),...p(n)

*/

Hdu 4658 要求拆分的数中每个数出现的次数不能大于等于k次,则


已经求得,现在看Q(x^k)会怎么样

例如,当n=8k=4

满足指数为8的乘积之和为:

所以将8拆分的数中每个数的个数小于4的有16个,分别为

8,1+7,1+1+6,1+1+1+5,6+2,1+5+2,1+1+4+2,1+1+1+3+2,4+2+2,1+3+2+2,1+1+2+2+2,1+1+3+3,1+4+3,5+3,2+3+3,4+4



hdu 4651

http://acm.hdu.edu.cn/showproblem.php?pid=4651 

代码有点捉急。。

[cpp]  view plain copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<string>  
  5. #include<cmath>  
  6. #include<algorithm>  
  7.   
  8. using namespace std;  
  9.   
  10. typedef __int64 LL;  
  11. const int maxn=100010;  
  12. const LL MOD=1000000007;  
  13. LL dp[maxn];  
  14. LL Five(LL x)  
  15. {  
  16.     LL ans=3*x*x-x;  
  17.     return ans/2;  
  18. }  
  19. void _init()  
  20. {  
  21.     dp[0]=1;  
  22.     for(int i=1;i<maxn;i++)  
  23.     {  
  24.         for(int j=1;;j++)  
  25.         {  
  26.             LL k=Five(j);//获取五边形数  
  27.             if(k<=i)  
  28.             {  
  29.                 if(j%2==0)//判断奇偶从而判断系数为正还是为负  
  30.                     dp[i]=(dp[i]-dp[i-k]);  
  31.                 else  
  32.                     dp[i]=(dp[i]+dp[i-k]);  
  33.                 if(dp[i]>=MOD) dp[i]%=MOD;  
  34.                 if(dp[i]<0) dp[i]+=MOD;  
  35.                 k=Five(-1*j);  
  36.                 if(k<=i)  
  37.                 {  
  38.                     if(j%2==0)  
  39.                         dp[i]=(dp[i]-dp[i-k]);  
  40.                     else  
  41.                         dp[i]=(dp[i]+dp[i-k]);  
  42.                     if(dp[i]>=MOD) dp[i]%=MOD;  
  43.                      if(dp[i]<0) dp[i]+=MOD;  
  44.                 }  
  45.                 else  
  46.                     break;  
  47.             }  
  48.             else  
  49.                 break;  
  50.         }  
  51.     }  
  52. }  
  53. int main()  
  54. {  
  55.     _init();  
  56.     int T,n;  
  57.     scanf("%d",&T);  
  58.     while(T--)  
  59.     {  
  60.         scanf("%d",&n);  
  61.         printf("%I64d\n",dp[n]);  
  62.     }  
  63.     return 0;  
  64. }  

hdu 4658

http://acm.hdu.edu.cn/showproblem.php?pid=4658 

[cpp]  view plain copy
  1. #include<iostream>  
  2. #include<cstdio>  
  3. #include<cstring>  
  4. #include<string>  
  5. #include<cmath>  
  6. #include<algorithm>  
  7.   
  8. using namespace std;  
  9.   
  10. typedef __int64 LL;  
  11. const int Maxn=100010;  
  12. const LL MOD=1000000007;  
  13. LL Q[Maxn],P[Maxn];  
  14. LL GetQ(LL x)  
  15. {  
  16.     LL ans=(LL)x*x*3-x;  
  17.     return (ans/2)%MOD;  
  18. }  
  19. void _init()  
  20. {  
  21.     Q[0]=0;  
  22.     for(int i=1;i<Maxn;i++)  
  23.     {  
  24.         if(i&1) Q[i]=GetQ(i/2+1);  
  25.         else Q[i]=GetQ(i/2*(-1));  
  26.     }  
  27.     P[0]=P[1]=1;  
  28.     for(int i=2;i<Maxn;i++)  
  29.     {  
  30.         for(int j=1;;j++)  
  31.         {  
  32.             if(Q[j]>i) break;  
  33.             int t=j;  
  34.             if(t&1) t=t/2+1;  
  35.             else t=t/2;  
  36.             if(t&1)  
  37.                 P[i]=(P[i]+P[i-Q[j]]);  
  38.             else  
  39.                 P[i]=(P[i]-P[i-Q[j]]);  
  40.             if(P[i]>=MOD) P[i]%=MOD;  
  41.             if(P[i]<0) P[i]+=MOD;  
  42.         }  
  43.     }  
  44. }  
  45. void solved(LL n,LL k)  
  46. {  
  47.     LL ans=0;  
  48.     for(int i=0;;i++)  
  49.     {  
  50.         if(Q[i]*k>n) break;  
  51.         int t=i;  
  52.         if(t&1) t=t/2+1;  
  53.         else t=t/2;  
  54.         if(t&1) ans=(ans-P[n-Q[i]*k]);  
  55.         else ans=(ans+P[n-Q[i]*k]);  
  56.         if(ans>=MOD) ans%=MOD;  
  57.         if(ans<0) ans+=MOD;  
  58.     }  
  59.     printf("%I64d\n",ans);  
  60. }  
  61. int main()  
  62. {  
  63.     _init();  
  64.     int T;  
  65.     LL n,k;  
  66.     scanf("%d",&T);  
  67.     while(T--)  
  68.     {  
  69.         scanf("%I64d%I64d",&n,&k);  
  70.         solved(n,k);  
  71.     }  
  72.     return 0;  
  73. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值