hdu 1066 数论+递归

来源:互联网 发布:浏览器代理软件自动 编辑:程序博客网 时间:2024/05/29 14:17
HDU 1066 Last non-zero Digit in N!

起首引用下leemars的呈报:

这道题请求N!的最后一个非0数字是几许,若是用一般作法,先统计2和5的个数,然
后补乘2,获得的将是TLE。所以还须要再做简化:
为了把0去掉,我们把所有的因数2和5都提出来,放到最后再处理惩罚。N!中的N个相乘的
数可以分成两堆:奇数和偶数。偶数相乘可以写成(2^M)*(M!),M=N DIV 2。M!可以
递归处理惩罚,是以如今只需评论辩论奇数相乘。推敲1*3*5*7*9*11*13*15*17* ... *N(若是
N为偶数则是N-1),这里面是5的倍数的有5,15,25,35,... ,可以此中的5提出来
,变成(5^P)*(1*3*5*7*9* ... ),后面括号中共P项,P=(N DIV 5+1) DIV 2,而后
面的括号又可以持续提5出来,递归处理惩罚。如今剩下的数是1 * 3 * 7 * 9 * 11 * 13
* 17 * 19 * ... 。这些数我们只须要他们的个位数,因为(1 * 3 * 9 * 11 * 13
* ... ) MOD 10 = (1 * 3 * 7 * 9 * 1 * 3 * ... ) MOD 10。我们列出余数表,
1 3 1 9 9 7 9 1 1 3 1 9 9 7 9 ……。我们发明每八项MOD 10的成果是一个轮回。
算出奇数的成果后,我们再回头看统计了几许个2和5须要乘入。把2和5配对完都是N
!后面的0,看剩下的2有几个。若是有剩下的2,推敲2^N的个位数又是2 4 8 6 2 4
8 6 ……每四项一个轮回,找出这个个位数后,和前面的成果相乘,再取个位数就是
答案。因为我们完全把2和5的身分别的处理惩罚,所以在所有的乘法中,都只须要策画个位数乘法,并且只保存个位数的成果。

但让我很惊奇的是:为什么我提交老是WA?后来我才知道,原因是这道题的N相当大
!达到了10^100!要用大数来处理惩罚!GPC四项编译开关全关的,天然查不出来!并且
上方这个算法换成大数后会很麻烦。还有什么此外好办法吗?
答案是有的。我们设F(N)默示N!的尾数。
先推敲简单的。推敲某一个N!(N < 10),我们先将所有5的倍数提出来,用1庖代本来
5的倍数的地位。因为5的倍数全被提走了,所以如许就不会呈现尾数0了。我们先把
0..9的阶乘的尾数列出来(重视,5的倍数的地位上是1),可以获得table[0..9] =
(1, 1, 2, 6, 4, 4, 4, 8, 4, 6)。对于N < 5,直接输出table[N]即可;对于N >
= 5,因为提出了一个5,是以须要一个2与之配成10,即将尾数除以2。重视到除了0
!和1!,阶乘的最后一个非零数字必为偶数,所以有一个很特此外除律例律:2 / 2
= 6,4 / 2 = 2,6 / 2 = 8,8 / 2 = 4。斗劲特别的就是2 / 2 = 12 / 2 = 6,
6 / 2 = 16 / 2 = 8。如许我们就可以获得如下式子:
代码:
               table[N]
F(N) = ------------ (0 <= N < 10)
               2^([N/5])
再推敲错杂的。推敲某一个N!(N >= 10),我们先将所有5的倍数提出来,用1庖代原
来5的倍数的地位。因为5的倍数全被提走了,所以如许就不会呈现尾数0了。我们观
察一下剩下的数的乘积的尾数,经由过程table表,我们发明这10个数的乘积的尾数是6,
6 * 6的尾数还是6,是以我们将剩下的数每10个分成一组,则剩下的数的乘积的尾数
只与最后一组的景象有关,即与N的最后一位数字有关。因为我们把5的倍数提出来了
,N!中一次可以提出[N/5]个5的倍数,有几许个5,就须要有几许个2与之配成10,所
以有几许个5,最后就要除以几许个2。重视到除2的成果变更是4个一轮回,是以若是
有A个5,只须要除(A MOD 4)次2就可以了。A MOD 4只与A的最后两位数有关,很好求
算。剩下的5的倍数,因为5已经全部处理惩罚掉了,就变成[N/5]!。于是,我们可以获得
一个递归关系:
代码:
               F([N/5]) * table[N的尾数] * 6
F(N) = ----------------------------------- (N > 10)
               2^([N/5] MOD 4)
如许我们就获得了一个O(log5(N))的算法,整除5可以用高精度加法做,乘2再除10即
可。全部算法相当奇妙,写起来也斗劲轻松。

因为 2^N 是以4为轮回节的

并且table[N]是以10为轮回节的

所以从10开端

               F([N/5]) * table[N的尾数] * 6
F(N) = ----------------------------------- (N > 10)
               2^([N/5] MOD 4)

右边的式子除了F[n/5]外 是以20为轮回节的

写出轮回的末尾数字mod[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2}

整体思路解决了

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#include<cstring>
#include<vector>
#include<string>
#define LL long long
using namespace std;
int mod[20]={1,1,2,6,4,2,2,4,2,8,4,4,8,4,6,8,8,6,8,2};
int main(  )
{
    char str[1024];
    int num[1024];
    while( scanf( "%s",str )==1 )
    {
          int len = strlen( str ),ans=1;
          for( int i = 0 ; i < len ;  i++ )
               num[i] = str[len-i-1] - '0';
          while( len )
          {
             len -= !num[len-1];
             ans = ans*mod[num[1]%2*10 + num[0]]%10;
             for( int i = len -1,c=0;i >=0 ; i -- ){
                    c = c*10 + num[i];
                    num[i] = c/5;//倍数
                    c %= 5;//余数
                    }   
          }   
          printf( "%d\n",ans );
    }
    //system( "pause" );
    return 0;
}

0 0
原创粉丝点击