zoukankan      html  css  js  c++  java
  • hdu 5976 Detachment 逆元的应用

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5976

    题目要求将一个数n分解成若干个不同的整数,使得他们的乘积最大,我们知道,任何数x想要分解成n个数使得乘积最大就要将每个数变成x/n,如果不限制数的数量就尽可能多的分成三,剩下的分成二,如果要求数不能相同的话就要尽量将数分成连续的整数段。

    按照以往经验,必然是取一段连续自然数能够使得乘积最大,而这段连续自然数可从2开始(为啥不从1开始?从1开始还不如将这个1给这段连续自然数的最后一个数),于是我们可以得到形如2+3+4+...+k(k=2,3,...)的式子,而x是10^9内的任意整数,我们不可能恰好能够凑成连续自然数之和,可能会多出△x

    而这个△x的值,我可以保证它的范围为0≤△x≤k,相信大于等于0还是好理解的,为什么会小于等于k呢?因为当它大于k时,原式不是可以增加一项?即2+3+4+...+k+(k+1)

    那么多出来的△x怎么处理呢?显然是从后往前均摊给连续自然数中的(k-1)个数,为啥从后往前?因为若我们从前往后,总是会使连续自然数重复,不好处理

    于是,在我们分配完△x之后,我们大致会得到下述两种式子:

    ①2*3*...*(i-1)*(i+1)*...*k*(k+1)

    ②3*4*...*i*(i+1)*...*k*(k+2)

    显然,我们要计算此结果,可以借助阶乘,而阶乘中缺失的项,我们除掉就可以了,那么便会涉及除法取模,显然需要用到乘法逆元

    代码如下:其中求逆元用的是费马小定理

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef unsigned int ui;
     4 typedef long long ll;
     5 typedef unsigned long long ull;
     6 #define pf printf
     7 #define mem(a,b) memset(a,b,sizeof(a))
     8 #define prime1 1e9+7
     9 #define prime2 1e9+9
    10 #define pi 3.14159265
    11 #define lson l,mid,rt<<1
    12 #define rson mid+1,r,rt<<1|1
    13 #define scand(x) scanf("%llf",&x) 
    14 #define f(i,a,b) for(int i=a;i<=b;i++)
    15 #define scan(a) scanf("%d",&a)
    16 #define mp(a,b) make_pair((a),(b))
    17 #define P pair<int,int>
    18 #define dbg(args) cout<<#args<<":"<<args<<endl;
    19 #define inf 0x7ffffff
    20 inline int read(){
    21     int ans=0,w=1;
    22     char ch=getchar();
    23     while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    24     while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
    25     return ans*w;
    26 }
    27 int n,m,t;
    28 const int maxn=1e5+10;
    29 const ll mod = 1e9+7;
    30 ll sum[maxn],mul[maxn]; 
    31 int top=0;
    32 ll inv(ll a ,int k)//费马小定理在O(logn)时间内求解逆元  
    33 {
    34     ll ans= 1;
    35     while(k)
    36     {
    37         if(k&1)ans=(ans*a)%mod;
    38         k>>=1;
    39         a=(a*a)%mod;
    40     }
    41     return ans;
    42 }
    43 void init()//处理前缀和以及前缀积 
    44 {
    45     sum[1]=1;
    46     mul[1]=1;
    47     top=1;
    48     ll num=0,mu=1,i=2;
    49     while(num<=1000000000)
    50     {
    51         num=num+i;
    52         sum[++top]=num;//从2开始累加 
    53         mu=mu*i%mod;
    54         mul[top]=mu;
    55         i++;
    56     }
    57 }
    58 int main()
    59 {
    60     //freopen("input.txt","r",stdin);
    61     //freopen("output.txt","w",stdout);
    62     std::ios::sync_with_stdio(false);
    63     init();
    64     t=read();
    65     while(t--)
    66     {
    67         n=read();
    68         if(n==1)
    69         {
    70             pf("1
    ");
    71             continue;
    72         }
    73         ll ans=0; 
    74         //查找最后一个小于等于n的位置
    75         int k=upper_bound(sum+1,sum+top+1,n)-sum-1;
    76         int s=n-sum[k];//剩余的数 
    77         if(s==k)//此时是 3*4...(k+1)*(k+2)形式 
    78         {
    79             ans=(mul[k]*inv(2,mod-2)%mod*(k+2))%mod;
    80          }
    81          else //此时是2*3*...*(i-1)*(i+1)*...*(k+1) 
    82          {
    83              ans=(mul[k+1]*inv(mul[k-s+1],mod-2)%mod*mul[k-s])%mod;
    84           } 
    85         pf("%lld
    ",ans);
    86     }
    87 } 
  • 相关阅读:
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 回形取数
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    Java实现 蓝桥杯VIP 基础练习 报时助手
    block的是发送信号的线程,又不是处理槽函数的线程
  • 原文地址:https://www.cnblogs.com/randy-lo/p/12750478.html
Copyright © 2011-2022 走看看