hdu 5976 Detachment 逆元的应用

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5976

题目要求将一个数n分解成若干个不同的整数,使得他们的乘积最大,我们知道,任何数x想要分解成n个数使得乘积最大就要将每个数变成x/n,如果不限制数的数量就尽可能多的分成三,剩下的分成二,如果要求数不能相同的话就要尽量将数分成连续的整数段。

按照以往经验,必然是取一段连续自然数能够使得乘积最大,而这段连续自然数可从2开始(为啥不从1开始?从1开始还不如将这个1给这段连续自然数的最后一个数),于是我们可以得到形如2+3+4+...+k(k=2,3,...)的式子,而x是10^9内的任意整数,我们不可能恰好能够凑成连续自然数之和,可能会多出△x

而这个△x的值,我可以保证它的范围为0≤△x≤k,相信大于等于0还是好理解的,为什么会小于等于k呢?因为当它大于k时,原式不是可以增加一项?即2+3+4+...+k+(k+1)

那么多出来的△x怎么处理呢?显然是从后往前均摊给连续自然数中的(k-1)个数,为啥从后往前?因为若我们从前往后,总是会使连续自然数重复,不好处理

于是,在我们分配完△x之后,我们大致会得到下述两种式子:

①2*3*...*(i-1)*(i+1)*...*k*(k+1)

②3*4*...*i*(i+1)*...*k*(k+2)

显然,我们要计算此结果,可以借助阶乘,而阶乘中缺失的项,我们除掉就可以了,那么便会涉及除法取模,显然需要用到乘法逆元

代码如下:其中求逆元用的是费马小定理

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef unsigned int ui;
 4 typedef long long ll;
 5 typedef unsigned long long ull;
 6 #define pf printf
 7 #define mem(a,b) memset(a,b,sizeof(a))
 8 #define prime1 1e9+7
 9 #define prime2 1e9+9
10 #define pi 3.14159265
11 #define lson l,mid,rt<<1
12 #define rson mid+1,r,rt<<1|1
13 #define scand(x) scanf("%llf",&x) 
14 #define f(i,a,b) for(int i=a;i<=b;i++)
15 #define scan(a) scanf("%d",&a)
16 #define mp(a,b) make_pair((a),(b))
17 #define P pair<int,int>
18 #define dbg(args) cout<<#args<<":"<<args<<endl;
19 #define inf 0x7ffffff
20 inline int read(){
21     int ans=0,w=1;
22     char ch=getchar();
23     while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
24     while(isdigit(ch))ans=(ans<<3)+(ans<<1)+ch-'0',ch=getchar();
25     return ans*w;
26 }
27 int n,m,t;
28 const int maxn=1e5+10;
29 const ll mod = 1e9+7;
30 ll sum[maxn],mul[maxn]; 
31 int top=0;
32 ll inv(ll a ,int k)//费马小定理在O(logn)时间内求解逆元  
33 {
34     ll ans= 1;
35     while(k)
36     {
37         if(k&1)ans=(ans*a)%mod;
38         k>>=1;
39         a=(a*a)%mod;
40     }
41     return ans;
42 }
43 void init()//处理前缀和以及前缀积 
44 {
45     sum[1]=1;
46     mul[1]=1;
47     top=1;
48     ll num=0,mu=1,i=2;
49     while(num<=1000000000)
50     {
51         num=num+i;
52         sum[++top]=num;//从2开始累加 
53         mu=mu*i%mod;
54         mul[top]=mu;
55         i++;
56     }
57 }
58 int main()
59 {
60     //freopen("input.txt","r",stdin);
61     //freopen("output.txt","w",stdout);
62     std::ios::sync_with_stdio(false);
63     init();
64     t=read();
65     while(t--)
66     {
67         n=read();
68         if(n==1)
69         {
70             pf("1\n");
71             continue;
72         }
73         ll ans=0; 
74         //查找最后一个小于等于n的位置
75         int k=upper_bound(sum+1,sum+top+1,n)-sum-1;
76         int s=n-sum[k];//剩余的数 
77         if(s==k)//此时是 3*4...(k+1)*(k+2)形式 
78         {
79             ans=(mul[k]*inv(2,mod-2)%mod*(k+2))%mod;
80          }
81          else //此时是2*3*...*(i-1)*(i+1)*...*(k+1) 
82          {
83              ans=(mul[k+1]*inv(mul[k-s+1],mod-2)%mod*mul[k-s])%mod;
84           } 
85         pf("%lld\n",ans);
86     }
87 } 

猜你喜欢

转载自www.cnblogs.com/randy-lo/p/12750478.html