http://acm.hdu.edu.cn/showproblem.php?pid=5976
解题思路:
首先需要判断怎么分割才能保证乘积最大?对于连续数,我们从2开始
2+3
2+3+4
2+3+4+5
….
2+3+4+….+m
其中m满足 2*m+(m-1)*m/2 >= 1e9 大概是1e5的样子
对于一个数n,如果不能恰好等于从2开始一直到k的连续数之和,那么就找到这个最大的k,满足 2+3+..+k<=n ,剩下的余数,就从k往前每个数都累加一次,一直到余数用完。其中,必然有一个数是空着的。比如当n=12的时候,k为4 (即2+3+4=9),余数为3,那么就有(3+4+5=12) 其中2便是空着的没有算进去,因此需要除去,即4!/2。
这样,我们便可以预处理好所有连续数的和,已经每个数的阶乘。对于每个输入的n,找到最大的k,通过求和取余找到那个需要除去的数,进行费马小定理求逆元,利用快速幂,即可求出答案。
#include<iostream>
#include<algorithm>
#include<map>
#include<vector>
#include<set>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define mod 1000000007
typedef long long ll;
const int maxn=1e5+10;
ll sum[maxn+10],p[maxn+10];
ll quick_mod(ll x,ll n)
{
ll res=1;
while(n)
{
if(n%2)
res=res*x%mod;
x=x*x%mod;
n/=2;
}
return res;
}
void Init()
{
memset(sum,0,sizeof(sum));
sum[3]=5;
for(int i=4;i<=maxn;i++)
sum[i]=sum[i-1]+i;
p[1]=1;
for(int i=2;i<=maxn;i++)
p[i]=p[i-1]*i%mod;
}
int main()
{
Init();
int t;
ll n;
scanf("%d",&t);
while(t--)
{
scanf("%lld",&n);
if(n<5)
{
printf("%lld\n",n);
continue;
}
int k=upper_bound(sum,sum+maxn,n)-sum;
ll m=n-sum[k-1],ans;
if(m==k-1)
ans=p[k+1]*quick_mod(2,mod-2)%mod*quick_mod(m+1,mod-2)%mod;
else
ans=p[k]*quick_mod(k-m,mod-2)%mod;
printf("%lld\n",ans);
}
return 0;
}