/*
hdu 5976
题目大意是
给你一个数x
将它分解成任意个数之和的形式,但这些数不能相同
即x= a1+a2+……+an(ai!=aj)
然后将分解的数相乘使它最大输出这个数
即使s=a1*a2*……*an最大,输出s
解题思路
一个>=5的数分解成若干个数之和
我们要乘积最大
那么我们把x尽可能的拆分
所以全部拆成2和3最好
但是题目要求拆成的数不能相同
所以我们要尽量拆成2+3+4+……+l
当拆分成上述式子之和
可能会剩下k(0<=k<=l,因为当大于l之后就要加一项l+1)
这个时候可以把k看成k个1
然后将上式从后往前每一项依次+1
因为如果从前面加的话就会出现重复
当k<l-1时
肯定有些项没法+1
在没法+1的第一项之前的都已经+1
所以肯定会出现这种情况ai-aj==2
也就是说会缺失一项s=l-k+1
所以我们要求的就是(l+1)!/s%mod即可
当k==l-1时
刚好所有的都已经+1
所以我们要求的就是(l+1)!/2%mod即可
当k==l时
所有的都已经+1
且第一项即原来的l项加了2
而且缺失了2和l+1这两项
所以我们要求的就是(l+2)!/(2*l+2)%mod即可
所以根据上述思路
先求没有1的前缀和sum[]和前缀积mul[]
前缀和用来判断找到的l满不满足要求
前缀积用来求阶乘
之后我们现在要做的就是找到l
因为k=x-sum[l]
然后用二分找到l
最后按照上述思路找到缺项
求出缺项的逆元与mul[l]相乘即可
最后不要忘了上述思路是针对x>=5的情况
当0<x<5时直接输出x即可
*/
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <string.h>
#define MAX 3
#define mod 1000000007
#define ll long long
using namespace std;
ll sum[50005];
ll mul[50005];
void init()//求前缀和前缀积
{
sum[1]=0;
mul[1]=1;
for(int i=2;i<=50000;i++)
{
sum[i]=sum[i-1]+i;
mul[i]=(mul[i-1]*i)%mod;
}
}
void Ex_gcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1;
y = 0;
return;
}
int x1, y1;
Ex_gcd(b, a%b, x1, y1);
x = y1;
y = x1-(a/b)*y1;
}
int main()
{
init();
int t;
cin>>t;
while(t--)
{
int n;
scanf("%d",&n);
if(n<5)//特判
{
printf("%d\n",n);
continue;
}
int l=0,r=50000,mid;
while(r-l>1)//二分查找,可以粗略求一下50000的前缀和肯定大于10^9
{
mid=(l+r)/2;
if(sum[mid]>n)
r=mid;
else
l=mid;
}
int k=n-sum[l];
int ans,x,y;
if(k==l)//根据思路分三种情况
{
ll s=((2*l)%mod+2)%mod;
Ex_gcd(s,mod,x,y);
x=(x+mod)%mod;
ans=(mul[l+2]*x)%mod;
printf("%d\n",ans);
continue;
}
if(k==l-1)
{
Ex_gcd(2,mod,x,y);
x=(x+mod)%mod;
ans=(mul[l+1]*x)%mod;
printf("%d\n",ans);
continue;
}
ll s=l-k+1;
Ex_gcd(s,mod,x,y);
x=(x+mod)%mod;
ans=(mul[l+1]*x)%mod;
printf("%d\n",ans);
}
return 0;
}
hdu 5976
题目大意是
给你一个数x
将它分解成任意个数之和的形式,但这些数不能相同
即x= a1+a2+……+an(ai!=aj)
然后将分解的数相乘使它最大输出这个数
即使s=a1*a2*……*an最大,输出s
解题思路
一个>=5的数分解成若干个数之和
我们要乘积最大
那么我们把x尽可能的拆分
所以全部拆成2和3最好
但是题目要求拆成的数不能相同
所以我们要尽量拆成2+3+4+……+l
当拆分成上述式子之和
可能会剩下k(0<=k<=l,因为当大于l之后就要加一项l+1)
这个时候可以把k看成k个1
然后将上式从后往前每一项依次+1
因为如果从前面加的话就会出现重复
当k<l-1时
肯定有些项没法+1
在没法+1的第一项之前的都已经+1
所以肯定会出现这种情况ai-aj==2
也就是说会缺失一项s=l-k+1
所以我们要求的就是(l+1)!/s%mod即可
当k==l-1时
刚好所有的都已经+1
所以我们要求的就是(l+1)!/2%mod即可
当k==l时
所有的都已经+1
且第一项即原来的l项加了2
而且缺失了2和l+1这两项
所以我们要求的就是(l+2)!/(2*l+2)%mod即可
所以根据上述思路
先求没有1的前缀和sum[]和前缀积mul[]
前缀和用来判断找到的l满不满足要求
前缀积用来求阶乘
之后我们现在要做的就是找到l
因为k=x-sum[l]
然后用二分找到l
最后按照上述思路找到缺项
求出缺项的逆元与mul[l]相乘即可
最后不要忘了上述思路是针对x>=5的情况
当0<x<5时直接输出x即可
*/
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <math.h>
#include <string.h>
#define MAX 3
#define mod 1000000007
#define ll long long
using namespace std;
ll sum[50005];
ll mul[50005];
void init()//求前缀和前缀积
{
sum[1]=0;
mul[1]=1;
for(int i=2;i<=50000;i++)
{
sum[i]=sum[i-1]+i;
mul[i]=(mul[i-1]*i)%mod;
}
}
void Ex_gcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1;
y = 0;
return;
}
int x1, y1;
Ex_gcd(b, a%b, x1, y1);
x = y1;
y = x1-(a/b)*y1;
}
int main()
{
init();
int t;
cin>>t;
while(t--)
{
int n;
scanf("%d",&n);
if(n<5)//特判
{
printf("%d\n",n);
continue;
}
int l=0,r=50000,mid;
while(r-l>1)//二分查找,可以粗略求一下50000的前缀和肯定大于10^9
{
mid=(l+r)/2;
if(sum[mid]>n)
r=mid;
else
l=mid;
}
int k=n-sum[l];
int ans,x,y;
if(k==l)//根据思路分三种情况
{
ll s=((2*l)%mod+2)%mod;
Ex_gcd(s,mod,x,y);
x=(x+mod)%mod;
ans=(mul[l+2]*x)%mod;
printf("%d\n",ans);
continue;
}
if(k==l-1)
{
Ex_gcd(2,mod,x,y);
x=(x+mod)%mod;
ans=(mul[l+1]*x)%mod;
printf("%d\n",ans);
continue;
}
ll s=l-k+1;
Ex_gcd(s,mod,x,y);
x=(x+mod)%mod;
ans=(mul[l+1]*x)%mod;
printf("%d\n",ans);
}
return 0;
}