HDU 5976 逆元
2、题意:给一个正整数x,把x拆分成多个正整数的和,这些数不能有重复,要使这些数的积尽可能的大,输出积。
3、总结:首先我们要把数拆得尽可能小,这样积才会更大(当然不能拆1)。所以容易想到是拆成2+3+...+n+s=x,先求出n即2+3+...+n<x<2+3+...+n+(n+1),然后将某个数向右平移s个单位变为n+1即可。注意:(1)预处理出前缀和,前缀积。(2)将某个数移到n+1,要除这个数再乘n+1,这里要用逆元,也要预处理出来。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define F(i,a,b) for (int i=a;i<b;i++) #define FF(i,a,b) for (int i=a;i<=b;i++) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f typedef long long ll; const int N = 1e5+10; const ll mod = 1e9+7; int sum[N],inv[N]; ll mul[N]; //int tot; void init() { sum[1]=0; mul[1]=inv[1]=1; F(i,2,N) { sum[i]=i+sum[i-1]; //前缀和 mul[i]=i*mul[i-1]%mod; //前缀积 inv[i]=(mod-mod/i)*inv[mod%i]%mod; //O(n)时间内求逆元,要求mod是素数 //tot=i; } } int main() { int T,x; scanf("%d", &T); init(); while(T--) { scanf("%d",&x); if(x<5) printf("%d\n", x); else { /* int l=lower_bound(sum+1,sum+1+tot,x)-sum; //可以直接函数二分,找出第一个大于或等于x的位置 if(sum[l]==x) { printf("%d\n", mul[l]); continue; } l--; */ int l=2,r=N,mid=(l+r)>>1; //特注:二分写法,mid先定义,不要在while里面 while(l+1<r) { //mid=(l+r)>>1; //注:一开始这样写,错得莫名其妙,还是养成习惯,先在上面定义mid sum[mid]>x ? r=mid : l=mid; mid=(l+r)>>1; } ll ans; int k,num; num=x-sum[l], k=l+1-num; if(2+num>l) ans=mul[l]*inv[2]%mod*(2+num)%mod; else ans=mul[l]*inv[k]%mod*(l+1)%mod; printf("%lld\n", (ans+mod)%mod); } } return 0; }