HDU 4651 Partition

本文介绍了一种计算从1到15的数字相加等于15的组合方式数量的方法,并提供了求模运算的实现。通过利用五边形数理论和欧拉函数,实现了快速计算任意范围内数字组合的数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Partition

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 117    Accepted Submission(s): 61


Problem Description
How many ways can the numbers 1 to 15 be added together to make 15? The technical term for what you are asking is the "number of partition" which is often called P(n). A partition of n is a collection of positive integers (not necessarily distinct) whose sum equals n.

Now, I will give you a number n, and please tell me P(n) mod 1000000007.
 

Input
The first line contains a number T(1 ≤ T ≤ 100), which is the number of the case number. The next T lines, each line contains a number n(1 ≤ n ≤ 105) you need to consider.

 

Output
For each n, output P(n) in a single line.
 

Sample Input
4
5
11
15
19
 

Sample Output
7
56
176
490


五边形定理:(1-x)(1-x^2)(1-x^3)....(1-x^n) = 1-x-x^2+x^5+x^7-x^12-x^15+...,其中0,1,2,5,7,12,15..称之为广义五边形数。

其中(1-x)(1-x^2)..(1-x^n)为欧拉函数,欧拉函数的倒数是分割函数的母函数,既有(1-x)(1-x^2)...(1-x^n)*(1+p1*x+p2*x^2+..pn*x^n) = 1; 其中pk为k的分割函数。根据系数可以得到p(n)=p(n-1)+p(n-2)-p(n-5)-p(n-7)+p(n-12)+p(n-15)...比如p(10)*x^10+p(9)*x^9*(-x)+p(8)*x^8*(-x^2)+p(5)*x^5*(x^5)+p(3)*x^3*(x^7)=0,其中-x,-x^2,x^5,x^7中1,2,5,7便是广义五边形数。上式移项便得到递推公式p(n)=p(n-1)+p(n-2)-p(n-5)-p(n-7)+p(n-12)+p(n-15)-...

所以,首先将广义五边形数打表,广义五边形的通项公式为(3*n*n-n)/2,(3*n*n+n)/2。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 1000000007

using namespace std;

typedef long long LL;

LL pent[666],idx;
LL p[100010];

int main()
{
    idx = 0;
    for(LL i=1; i<=300; i++) //打表,广义五边形数
    {
        pent[++idx] = (3*i*i-i)/2;
        pent[++idx] = (3*i*i+i)/2;
    }
    memset(p,0,sizeof(p));
    p[0] = 1;
    for(int i=1; i<=100000; i++) //把分割函数打表,即是答案
    {
        int tim = 0;
        for(int j=1; j<=601; j++)
        {
            if(pent[j] > i)
                break;
            if(tim % 4 <= 1) //先加两次
            {
                p[i] = (p[i] + p[i-pent[j]])%mod;
                tim ++;
            }
            else  
            {
                p[i] = p[i] - p[i-pent[j]]; //后减两次,然后循环
                if(p[i] < 0)
                    p[i] = p[i]%mod + mod;
                tim ++;
            }
        }
    }
    int cas;
    scanf("%d",&cas);
    while(cas--)
    {
        LL tar;
        scanf("%I64d",&tar);
        printf("%I64d\n",p[tar]);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值