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;
}

猜你喜欢

转载自blog.csdn.net/qq_42937838/article/details/108437097