HDU - 5976 Detachment(ICPC2016 大连现场,脑洞加逆元)

题目描述:点击打开链接

题意也很简单,不解释。其实这题脑洞做法并不是我想出来的,队友想出来的做法我只负责了代码实现而已,关于做法,这题的分割方案举个例子比如7,7=2+3,后面加不了4了,并且还多出2那么我开始从3开始倒着每个数+1变成7=3+4所以答案就是3*4=12,在比如17=2+3+4+5,这是加不了6,并且还差3那么我们就把这3倒着每个数+1变成17=2+4+5+6,所以答案就是2*4*5*6=240,这样应该是解释的比较清楚了,那么讨论一下特殊情况就是我们从2开始累加完之后,还差的数字把每个数都加了1之后还差,比如13=2+3+4这里加不了5,那么把还差的4倒着每个数加1之后变成13=3+4+5这个时候还差1,那么就变成3+4+6这就是最终的答案,并且我们会发现这就是最极限的情况了,最多就是最后一个数+2,其他数都+1不会出现倒数第二个数甚至更前面的数+2的情况。那么讨论完做法之后,关于具体的实现,我们可以先把累加和打个表把阶乘也打个表,每次找到第一个大于该数x的位置然后-1就找到了前面有几个数,然后求出求出还差多少,讨论一下这个差值的情况如果不会出现最后一个数+2的情况的话那么只需要找到+1的最前面的一个位置就可以了,比如17我们找到2+3+4+5发现差2,补完之后是2+3+5+6实际上就是6!/4。而对于最后一个数+2的情况其实就是原来最后一个数+2的阶乘除2和原来最后最后一个数+1,比如13一开始我们找到2+3+4最后补完是3+4+6实际上就是6!/2*5。最后关于除法取模的问题,用费马小定理求一下逆元就好了。

AC代码:

#include<iostream>
#include<sstream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<stack>
#include<queue>
#include<algorithm>
using namespace std;
const long long MOD=1e9+7;
const int MAXM=45000;

long long x;
long long sum[MAXM+10];
long long mul[MAXM+10];

void init()
{
    sum[1]=0;
    for (long long i=2;i<=MAXM;i++)
        sum[i]=(sum[i-1]+i);
    mul[1]=1;
    for (long long i=2;i<=MAXM;i++)
        mul[i]=(mul[i-1]*i)%MOD;
}
long long qpow(long long x,long long y)
{
    long long res=1;
    while(y) {
        if (y&1) res=(res*x)%MOD;
        x=(x*x)%MOD;
        y=y>>1;
    }
    return res;
}
int main()
{
    int T;
    scanf("%d",&T);
    init();
    while(T--)
    {
        scanf("%lld",&x);
        if (x==1) {
            printf("1\n");
            continue;
        }
        int index=upper_bound(sum+2,sum+2+MAXM+10,x)-sum;
       // cout<<index<<endl;
        index--;
       // cout<<index<<endl;
        long long sub=x-sum[index];
        //cout<<sub<<endl;
        long long ans;
        if (sub==(long long)(index-1)) {
            ans=mul[index+1]*(qpow(2,MOD-2)%MOD)%MOD;
        }
        else if (sub==(long long)index) {
            ans=(mul[index+2])%MOD*qpow(2*(index+1)%MOD,MOD-2)%MOD;
            //cout<<ans<<endl;
        }
        else {
            ans=(mul[index+1])%MOD*qpow(index-sub+1,MOD-2)%MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值