这道题目是向这位博主学习的,链接贴出来:☆HDU 5976 Detachment 详细题解(贪心+逆元+前缀和,积),讲的很细致,很感谢;
思路:
1.题意是将数x
分成若干个正整数和,使得这些整数的乘积最大,并求出这个最大乘积除以1e9+7
的余数;
2.要使得乘积最大,那么我们需要尽可能的分出更多的数(1
除外),又因为分出的数需要各不相同,由贪心思想,我们可以从最小的2
开始往后分,一直分到k
,这个k
满足:(1)2+3+4+...+k<=x
(2)2+3+4+...+(k+1)>x
;
我们记前缀和数组sum[i]=2+3+...+i
,记ans=x-sum[k]
,
我们可以知道ans>=0&&ans<=k
;
3.此时我们已经将数大致分好,还差ans
、和就可以达到x
,此时的想法是将ans
分成若干个1
从后往前摊派到各个数上(如果从前往后摊派会造成数据重复),摊派完会根据ans
值的不同而出现两种情况:
(我们设f(x)
是x
模1e9+7
的逆)
(1)ans>=0&&ans<k
;
此时乘积是2*3*4*...*(r-1)*(r+1)*...*(k+1)
;
答案是(k+1)!/r%MOD
,即(k+1)!*f(r)%MOD
;
(2)ans==k
;
此时乘积是3*4*5*...*k*(k+2)
;
答案是(k+2)!/(k+1)/2%MOD
,即 (k+2)!*f(k+1)*f(2)%MOD
;
代码:
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAX_N=1e5;
const LL MOD=1e9+7;
LL mul[MAX_N],sum[MAX_N]; //前缀积、前缀和
LL extgcd(LL a,LL b,LL& x,LL& y){
LL d=a;
if(b){d=extgcd(b,a%b,y,x);y-=(a/b)*x;}
else{x=1;y=0;}
return d;
}
LL mod_inv(LL a,LL m){
LL x,y;
extgcd(a,m,x,y);
return (m+x%m)%m;
}
void init(){
mul[1]=1;
for(int i=2;i<MAX_N;i++) mul[i]=mul[i-1]*i%MOD;
for(int i=2;i<MAX_N;i++) sum[i]=sum[i-1]+i;
}
int main(){
init();
int t;
LL x;
scanf("%d",&t);
while(t--){
scanf("%lld",&x);
if(x==1){puts("1");continue;}
int k=(upper_bound(sum,sum+MAX_N,x)-sum)-1;
LL ans=x-sum[k];
if(ans<k){
LL r=k-ans+1;
printf("%lld
",mul[k+1]*mod_inv(r,MOD)%MOD);
}else{
printf("%lld
",mul[k+2]*mod_inv(k+1,MOD)%MOD*mod_inv(2,MOD)%MOD);
}
}
return 0;
}