zoukankan      html  css  js  c++  java
  • 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)=sumlimits_{i=1}^{infty} lfloor frac{n}{k^i} floor$其中,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
    ",ansans);
    71     return 0;
    72 }

        小结:错误:

          2A,第一次忘记特判2。

  • 相关阅读:
    删数问题
    装箱问题
    活动选择
    智力大冲浪
    三国游戏
    最大乘积
    排队接水
    线段覆盖
    高精度重载运算符
    数的划分
  • 原文地址:https://www.cnblogs.com/ShuraK/p/7892023.html
Copyright © 2011-2022 走看看