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\n",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",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\n",(ans+MOD)%MOD);
42                 else
43                 ans=f[l]*inv[l+1-k]%MOD*(l+1)%MOD,printf("%I64d\n",(ans+MOD)%MOD);
44             }
45         }
46     }
47     return 0;
48 }

 

posted @ 2016-11-10 00:05  晴雨天  阅读(1123)  评论(0编辑  收藏  举报