HDOJ 5976 Detachment【2016大连现场】【数学好题】

题意很简单:将一个整数n分解成很多不相同整数的和,使得这些整数乘积尽可能大。其中n最大1e9


第一眼看到这个题:

好简单啊,都分解成3啊!(没看到不相同)


第二眼:

可以猜想几个结论:

A:首先不可能有1,不要当作废话,那么意味着从2开始分解是有可能的

B:分解成尽可能多的数。个数多,比数大要好

举个例子:5=2+3,但是5如果不分解,答案是5;但是分解之后,5=2+3,答案为2*3=6

由这两个看似有道理的情况,说明我们要从2开始,不断往上加,来构造n:即:2+3+4+5+……

可惜的是:没有这么幸福的事,因为不相同,所以会有重复

比如7:7=2+3+2,剩下的这个2,怎么办:分解成1+1,往2+3上面分配就好:7=3+4

比如8:8=2+3+3,剩下的这个3,怎么办:分解成1+2,往2+3上面分配就好:8=3+5


所以,按照这个方法,我们猜想得到:这个题是一个构造题

为了方便自己计算,也为了方便最后编程之后得到更快的检验,我们先手算把简单的数字都算出来:

1=1

2=2

3=3

4=4

5=2+3

6=2+3+1=2+4

7=2+3+2=3+4

8=2+3+3=2+3+1+2=3+5

9=2+3+4

10=2+3+4+1=2+3+5

11=2+3+4+2=2+3+4+1+1=2+4+5

12=2+3+4+3=2+3+4+1+1+1=3+4+5

13=2+3+4+4=2+3+4+1+1+2=3+4+6

14=2+3+4+5

15=2+3+4+5+1=2+3+4+6


可以看到:分解数的个数是非递减的

意思是:我们可以根据输入的n,求出构造n的数位的长度len是多少

考虑到1+2+……+len是有公式的,我们可以算几个得到len


得到len之后,我们需要知道在1到len中少了哪几个数,多了哪几个外面的数

发现了几个规律:

当数字刚好是:2+3+……(len+1)的时候,刚好答案是(len+1)!

如果不是上面那种情况:那么只有两种情况:

A:缺1个数,可能缺2,3,4,等某一个

B:缺2个数,必然缺2,而且另一个是len

所以,我们把这些在的值乘起来就好


但是,这个题时限卡得很死

所以要用数学办法预处理好阶乘和逆元来搞

当答案跟len有关的时候,我们直接使用len的阶乘值

当要删去某个值的时候,我们直接乘那个数的逆元就好


代码如下:

#include<bits/stdc++.h>
using namespace std;

#define LL __int64
int t;
LL jiecheng[100050];
LL niyuan[100050];
LL x,mod=1e9+7;

LL qp(LL x,LL p){
    if (p==1) return x;
    LL res=1;
    while(p){
        if (p%2) res=(res*x)%mod;
        p/=2;
        x=x*x%mod;
    }
    return res;
}

int main(){
    //freopen("input.txt","r",stdin);
    jiecheng[1]=1;
    for(int i=2;i<=100000;i++)
        jiecheng[i]=(jiecheng[i-1]*i)%mod;
    for(int i=2;i<=100000;i++)
        niyuan[i]=qp(1LL*i,mod-2);
    scanf("%d",&t);
    for(int Case=1;Case<=t;Case++){
        scanf("%I64d",&x);
        LL len,sum;
        if (x<=4){
            printf("%I64d\n",x);
            continue;
        }
        else if (x==5){
            puts("6");
            continue;
        }
        //printf("%I64d:   ",x);
        len=(long long)(sqrt(2.0*x));
        while((1+len+1)*(1+len)>2*(1+x)) len--;
        //printf("%I64d %I64d\n",x,len);
        len++;
        sum=(1+len)*len/2;
        if (sum==x+1) printf("%I64d\n",jiecheng[len]);
        else{
            sum+=len;len++;
            if (sum==x+1){
                //printf("%I64d %I64d\n",jiecheng[len+1],2*(len));
                printf("%I64d\n",((jiecheng[len+1]*niyuan[2]%mod)*niyuan[len]%mod));
            }
            else{
                printf("%I64d\n",jiecheng[len]*niyuan[sum-x]%mod);
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值