zoukankan      html  css  js  c++  java
  • hdu5976贪心乘法逆元

    hdu 5976 Detachment题目连接

    题意:

    给定一个自然数x,让你给出一种拆分方式n=a1+a2+...(ai≠aj),使得每个小部分的乘积s=a1*a2*...最大

    解题思路:

    我们要乘积最大,那么我们把n尽可能的拆分,如果题目不要求ai≠aj,那么我们将x拆分成什么最好呢?显然拆成2和3(2为偶数,3为奇数,2x+3y可以表示大于1的所有正整数)是最好的,顺便提下3最多的时候最优,为什么不是2,将,6拆成3个2乘积为8,而将6拆成3乘积为9,(不会证)

    那这样我们就把n分成x个2和y个3,如果k=n-2x-3y>0&&k<2怎么办,此时k=1,把k放在哪里可以得到最优解呢?

    把k放在2上可以得到最优解。假设n=6,那么拆成一个2一个3,k=1,将k放在2上(2+k)*3增加了3*k,如果放在3上(3+k)*2增加了2*k,

    那么现在题目是要求ai≠aj

    我们将n分成2,3,4,5,6,7,8,。。。。。从2开始以递增分(可以分的话)

    我们设sum[max]为前缀和数组,那么当sum[i]>n时,k=n-sum[i-1];

    将k怎么放呢?(把k看成k个1)

    我们先放第一个1,放在谁哪里呢?显然放在2那,前面证过,所以为了最优我们尽可能的放在2,

    但当2+k<i,那么就会出现拆分的数重复出现的情况,所以能将k全部放在2是在2+k>=i的情况下

    如果在2+k<i的情况下该怎么放,因为此时2+k<i所以放任何个数的1都会出现重复的数,此时我们就要考虑3了,如果3+k>=i,我们全部放在3,

    如果3+k<i,我们考虑4,以此规则继续。。。。

    我们现在知道了,该怎么放多出来的k(如果有的话),那么最优乘积怎么求呢?

    我们要除掉k放在的那个数i,再乘以(k+i)

    为了让前缀乘不爆掉long long,我们取余,为了取余后不影响结果我们用乘法逆元来求最优乘积。

    我的乘法逆元博客

    用二分查找找i

    以下是代码实现

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #define max 1000000000
     5 const __int64 MOD=1000000007;//不能用define定义MOD否则会出错,define是一个函数
     6 using namespace std;
     7 __int64 f[45000];
     8 int sum[45000];
     9 int inv[45000];
    10 void del()
    11 {
    12     inv[1]=1;f[1]=1;sum[1]=0;
    13     for (int i = 2; i<45000; i++)
    14     {
    15         inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;//乘法逆元
    16         f[i]=(f[i-1]*i)%MOD;//前缀乘(在取余MOD的环境下,配合后面的乘法逆元)
    17         sum[i]=sum[i-1]+i;//前缀和(从2开始)
    18     }
    19     //printf("%d %d %d xxx
    ",inv[2],inv[3],inv[1]);
    20     return;
    21 }
    22 int main()
    23 {
    24     int T,n,i,j,k,l,r,mid;__int64 ans;
    25     while(scanf("%d",&T)!=EOF)
    26     {
    27         del();
    28         while(T--)
    29         {
    30             scanf("%d",&n);
    31             if(n<5)printf("%d
    ",n);
    32             else
    33             {
    34                 l=2;r=45000;mid=(l+r)>>1;
    35                 while(l+1<r)
    36                 {
    37                     if(sum[mid]>n)r=mid,mid=(r+l)>>1;//r定义为开,不取状态
    38                     else l=mid,mid=(r+l)>>1;//l定义为闭,取状态
    39                 }
    40                 k=n-sum[l];//printf("%d %d %d xx",sum[l],k,inv[l+1-k]);
    41                 if(2+k>l)ans=f[l]*inv[2]%MOD*(k+2)%MOD,printf("%I64d
    ",(ans+MOD)%MOD);
    42                 else
    43                 ans=f[l]*inv[l+1-k]%MOD*(l+1)%MOD,printf("%I64d
    ",(ans+MOD)%MOD);
    44             }
    45         }
    46     }
    47     return 0;
    48 }
  • 相关阅读:
    重载函规则
    lambd
    内联函数
    c和c++中的枚举和 区别
    关于于c++中的类型转换
    作用域解析运算符
    day01
    二级指针输入特性
    二级指针的 数出特性
    influence maximization 第二弹
  • 原文地址:https://www.cnblogs.com/WHLdbk/p/6049395.html
Copyright © 2011-2022 走看看