Factorials 阶乘
题目大意:给你一个数n,求出n ! 的最后一个非零位。
注释:n<=4200
想法:开始的想法是觉得这道题应该比较的有趣,因为我们知道,一个数的阶乘的最后的非零位后面或者是0,或者n<=4,所以,我们思考,如何才能有效的登出这个非零位。首先,我们发现,这个非零位后面零的个数是和n!中5的个数有关的,所以,我们思考:如果我们使得这个阶乘没有5会怎么样。想着想着,我相信你的头脑里会自然地蹦出一个定理——唯一分解定理。为什么?因为只有在这个定理的辅助下你才可以将5全部提取出来。我们又想到:由于唯一分解定理的存在,每个数都是有一个或几个定下来的素数组成,我们只需要这句话的一个性质:素数。一个数由素数组成,显然,这个素数是不大于本数的,n的数据范围是4200,是完全在我们的接受范围之内,想到这,这道题的大体轮廓就分为这样几个步骤:
1.筛出n之前的所有素数,由于n的数据范围过小,我们可以O ( n ) 的方法去筛。
2.对于每一个素数,我想求出n!中这个元素最多可以被整除多少次,也就是说我们到底有多少数包含多少这个素数。在此,介绍一个定理$f(n,k)=\sum\limits_{i=1}^{\infty} \lfloor \frac{n}{k^i}\rfloor$其中,f(n,k),表示n!中k的个数。
3.这么筛,显然不对,4200里面2的个数就够我们受的了,我们想得到一种优化,我们发现,我们其实只需要得到这个素数的最后一位即可。
4.但,还是有些困难,我们又发现了,对于每一个素数来讲(假设这个素数是a)$a^{4*k+i}=a^i$,我们只需处理%4意义下的即可。但是,a==2是需要特判。
呼~长出一口气,这题就切了。
最后,附上丑陋的代码......
1 #include <iostream> 2 #include <cstdio> 3 #include <cmath> 4 using namespace std; 5 int x[600]; 6 int ans[4178]; 7 int num(int a,int b)//计算素数在n!中的个数,这个函数表示b在a!中的个数 8 { 9 int ans=0; 10 while(a) 11 { 12 ans+=a/b; 13 a/=b; 14 } 15 return ans; 16 } 17 int power(int a,int b)//快速幂,其实可以直接乘,因为我们只考虑模4意义下 18 { 19 a%=10; 20 int ans=1; 21 while(b) 22 { 23 if(b&1) ans=(ans*a)%10; 24 b>>=1; 25 a=(a*a)%10; 26 } 27 return ans; 28 } 29 bool prime(int a)//判断是否为素数 30 { 31 int k=(int)(sqrt(a)); 32 bool flag=true; 33 for(int i=2;i<=k;i++) 34 { 35 if(a%i==0) 36 { 37 flag=false; 38 break; 39 } 40 } 41 return flag; 42 } 43 int main() 44 { 45 int n; 46 int cnt=0; 47 scanf("%d",&n); 48 for(int i=2;i<=n;i++)//筛素数 49 { 50 if(prime(i)) x[++cnt]=i; 51 } 52 for(int i=1;i<=cnt;i++)//用ans[]存素数个数 53 { 54 ans[x[i]]+=num(n,x[i]); 55 } 56 ans[2]-=ans[5];//我们再次用等数量的2将5替换掉,以便将最后的零去掉。 57 ans[5]=0; 58 int ansans=1; 59 for(int i=1;i<=cnt;i++)//对于每一个素数来讲,我们进行计算 60 { 61 ans[x[i]]%=4; 62 if(ans[x[i]]==0&&x[i]==2)//特判2,因为别的素数的4次方的最后一位都是1(5已经除去),但2不是 63 { 64 ansans*=6; 65 ansans%=10; 66 } 67 ansans*=power(x[i],ans[x[i]]); 68 ansans%=10;//我们只要最后一位 69 } 70 printf("%d\n",ansans); 71 return 0; 72 }
小结:错误:
2A,第一次忘记特判2。