题目链接: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\n"); 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\n",ans); 86 } 87 }