传送门
题意:给定一个数x,我们可以把这个数分解成一个一个的小的数字a1,a2,a3……
定义s=a1*a2*a3*…….
问如何分解x使得s最大,并且不能有重复的数字
思路:分解成数量多的小的数字,比分解成数量少的大的数字的乘积更大,这一点我不知道怎么证明……并且由基本不等式我们可以知道,相等和的两个数,越接近那么乘积越大
可想而知,如果可以有重复的数字,那我们可以把x分解成2和3的和即可,本题要求不能有重复的数字,原则是分解成更多小的数、更接近的数字,那么就可以2+3+4+…,但一般来说都不能正好分解完,会剩余一个数字,比如11=2+3+4+2,剩余了一个2,那我们把这个剩余的数字加到哪比较好呢,第一反应是直接加到最大的数字上,但仔细想想这样的话会扩大最后一个数与倒数第二个数的差,乘积可能会相对来说减少,所以我们要把这个数补在某个数上,使得补完后这个数接近之前的数群,比如11来说,我们把2补在3上,就变成了11=2+4+5,这样的乘积是比11=2+3+6的乘积大
如果我们定义可以分解成的最大的那个数为r,剩余的那个数为m,那么仔细想想就可以发现我们每次都可以把m补成r+1,除非m与r相等(因为前面没有1)
m=r的情况特判
m=r+1的情况也特判一下
其他情况就是r+1的前缀积除掉r+1-m,由于结果要对1e9+7取模,所以就是乘上r+1-m的逆元
两个需要优化的地方:前缀积要预处理一下,在找r时,用二分优化,求2,3,4……这个序列的前缀和,然后找x的位置,这里用了lower_bound函数
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MOD=1e9+7;
long long db[100001],db2[100001];
long long quick_mod(long long a,long long b)
{
long long ret=1;
while (b)
{
if (b&1)
ret=(ret*a)%MOD;
a=a*a%MOD;
b>>=1;
}
return ret;
}
int main()
{
int w;
int n,a,b,k,r;
long long ans;
db[2]=db2[2]=2;
for (int i=3;i<=100000;i++)
{
db[i]=(db[i-1]*i)%MOD;
db2[i]=db2[i-1]+i;
}
scanf("%d",&w);
while (w--)
{
scanf("%d",&n);
if (n<=4)
{
printf("%d\n",n);
continue;
}
r=lower_bound(db2,db2+100000,n)-db2;
if (db2[r]==n)
{
printf("%lld\n",db[r]);
continue;
}
else if(db2[r]-n==1)
{
ans=db[r-1]*quick_mod(2,MOD-2)%MOD;
ans=ans*(r+1)%MOD;
printf("%lld\n",ans);
continue;
}
n=n-db2[r-1];
k=r-n;
ans=db[r]*quick_mod(k,MOD-2)%MOD;
printf("%lld\n",ans);
}
return 0;
}