Factorials

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。

 

posted @ 2017-11-24 19:14  JZYshuraK_彧  阅读(333)  评论(0编辑  收藏  举报