HDU 5976 Detachment

 
这道题目是向这位博主学习的,链接贴出来:☆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)x1e9+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;
}
原文地址:https://www.cnblogs.com/yuhan-blog/p/12308836.html