HDU 5976 [数学]

//https://acm.hdu.edu.cn/showproblem.php?pid=5976
//题意:将一个数拆分成任意个不同的数,使得拆分出的数的积最大,输出积mod1e9+7
//数学思维+乘法逆元+前缀和预处理
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll MOD=1e9+7;
//第二版,需要优化查找x由哪些连续的数组成,以及连续数的乘积问题
//查找:可以预处理连续数的和,直接查找x在多少个数的和之间
//乘积:(参考网上做法)将连续的数的乘积视为阶乘,预处理出2到x^0.5的阶乘,如果不能连续则,算出不连续的那个数的逆元乘完取模
ll add[100005],mul[100005];
void init(){
    add[1]=0;
    for(int i=2;i<100000;i++){
        add[i]=add[i-1]+i;
    }
    mul[1]=1;
    for(int i=2;i<100000;i++){
        mul[i]=(mul[i-1]*i)%MOD;
    }
}
ll exgcd( ll a , ll b , ll &x , ll &y )
{
    if( !b ) // 当 b =0 时
    {
        x=1,y=0;
        return a;      
    }
    
    // 当 b!=0 时
    ll d=exgcd( b, a%b, x, y );
    ll t=x;
    
    x=y;
    y=t-(a/b)*y;
    
    return d;
}
/* 求逆元 a , m */
ll Inv_a(ll a,ll m)
{
    ll x,y;
    ll d=exgcd(a,m,x,y);
    
    if(d==1)
    {
        return ( x%m + m )%m; // 保证逆元为正数
    }
    else
       return -1;  // ax+my=1 不成立,即 a ,m  不互质,无解
    
}
int main(){
    init();
    int t;
    scanf("%d",&t);
    while(t--){
        ll x;
        scanf("%lld",&x);
        if(x>4){
            int n=upper_bound(add+1,add+100000,x)-add;
            n--;
            //printf("%lld",mul[n]);
            //printf("n=%d\n",n);
            ll temp=x-add[n];
            if(temp==0){
                printf("%lld\n",mul[n]);
            }
            else if(n==temp){
                printf("%lld\n",(mul[n]%MOD*Inv_a(2,MOD)%MOD*(n+2)%MOD)%MOD);
            }
            else{
                printf("%lld\n",(mul[n+1]*Inv_a(n-temp+1,MOD)%MOD));
            }

            // int  pos=upper_bound(add,add+50000,x)-add-1;
            // int num=x-add[pos];
            // long long x,y,ans;
            // if(num==pos)
            //     {
            //         ans=(mul[pos]%MOD*Inv_a(2,MOD)%MOD*(pos+2)%MOD)%MOD;
            //     }
            //     else if(num==0)
            //     ans=mul[pos];
            //     else
            //     {
            //         ans=(mul[pos+1]%MOD*Inv_a(mul[pos-num+1],MOD)%MOD*mul[pos-num]%MOD)%MOD;
            //     }
            //     printf("%lld\n",ans);
        }
        else{
            printf("%lld\n",x);
        }
    }
}
//注意点:取模时只要两个相乘就要再次取模

//第一版,复杂度为2*(x^0.5)*t,约为1e10,两秒明显超时
//明显分解一个数使乘积最大,且分解出的数不能相等,最好为从2开始的连续一串数(分析总结规律知,当不能刚好连续,最优为依次向大的数中加一)
//朴素做法,直接模拟,先分解x,再依次相乘
// int main(){
//     int t,ans;
//     scanf("%d",&t);
//     while(t--){//复杂度为1e6
//         int x,a[100005];
//         ll ans=1;

//         scanf("%lld",&x);
//         int k=0;
//         for(int i=2;i<x;i++){//相当于把x拆为连续的数的和,复杂度接近x^0.5
//             a[++k]=i;
//             x-=i;
//         }
//         int temp=k;
//         for(;x>0;x--){
//             a[temp--]++;
//             if(temp<1) temp=k;
//         }
//         for(int i=1;i<=k;i++){//复杂度也为x^0.5
//             ans=(ans*a[i])%MOD;
//         }
//         printf("%lld\n",ans);
//     }
//     return 0;
// }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值