HDU 5976 逆元,前缀和,数论

一些总结:
1)一个数字若是分成任意数字求乘积和最大,则尽量全部分成3
2)分成两个则是n/2
3)拆成n个,拆成这个数/n
4)不能重复,则优先拆为2,3,4…,剩余▲x从后往前平分。

逆元应用:https://www.cnblogs.com/WHLdbk/p/6051706.html
求逆元:
inv[1]=1;//1的逆元显然是1
for(i=2;i<n;i++)
inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD

#include<bits/stdc++.h>
#define max 1000000000
#define MOD (1000000007LL)
//const __int64 MOD=1000000007;//不能用define定义MOD否则会出错,define是一个函数
using namespace std;
__int64 f[45000];
int sum[45000];
int inv[45000];
void del()
{
    inv[1]=1;f[1]=1;sum[1]=0;
    for (int i = 2; i<45000; i++)
    {
        inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;///乘法逆元
        f[i]=(f[i-1]*i)%MOD;///前缀乘(在取余MOD的环境下,配合后面的乘法逆元)
        sum[i]=sum[i-1]+i;///前缀和(从2开始)
    }
    //printf("%d %d %d xxx\n",inv[2],inv[3],inv[1]);
    return;
}
int main()
{
    int T,n,k,l,r,mid;
    __int64 ans;
    del();
    while(~scanf("%d",&T))
    {
 
        while(T--)
        {
            scanf("%d",&n);
            if(n<5)printf("%d\n",n);
            else
            {
                l=2;r=45000;mid=(l+r)>>1;
                while(l+1<r) ///二分查找
                {
                    if(sum[mid]>n)
                        r=mid,mid=(r+l)>>1;///r定义为开,不取状态
                    else 
                        l=mid,mid=(r+l)>>1;///l定义为闭,取状态
                }
                k=n-sum[l];///printf("%d %d %d xx",sum[l],k,inv[l+1-k]);
                if(2+k>l)
                    ans=f[l]*inv[2]%MOD*(k+2)%MOD,printf("%I64d\n",(ans+MOD)%MOD);
                else
                    ans=f[l]*inv[l+1-k]%MOD*(l+1)%MOD,printf("%I64d\n",(ans+MOD)%MOD);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值