LETTers比赛第四场-Last non-zero Digit in N!
Last non-zero Digit in N!
该题数据较大,n可能有数百位,直接计算n!或通过n次计算都必然会超时。关键在于找出数字的规律。对于n<10,直接枚举即可,下面考虑n>=10的情况。
n!的尾部的0都来自因子5和因子2(一对5&2产生一个0),如果把这些因子去掉,则可符合要求。
定义F(n)为所要求的数,G(n)为1,2…n中将5的倍数的数换成1后的各项乘积(如:G(15)=1*2*3*4*1*6*7*8*9*1*11*12*13*14*1)(G(n)%10必不为0)。
则n!=(n/5)!*5^(n/5)*G(n),F(n)=F(n/5)*[5^(n/5)*G(n)%10],这可用递归处理,另外观察到5^(n/5)*G(n)%10=G(n)/2^(n/5)%10,这里G(n)%10(n>=10)其实是个周期为10的周期序列(程序中的table),G(n)/2^(n/5)必为偶数,这样得到一种特殊除法:8/2=4,4/2=2,2/2=6,6/2=8…,这样G(n)/[2^(n/5)]%10只与G(n)%10和(n/5)%4有关。而G(n)%10=table[n%10],n/5%4=n/5%100%4(%100好算,就是最后两位数字)。
#include<iostream> #include<string.h> using namespace std; #define MAXN 1000 char n[MAXN]; int b[MAXN]; const int m[10]={1,1,2,6,4,2,2,4,2,8}; // 对小于10的数直接得到非零尾数 const int table[10]={6,6,2,6,4,4,4,8,4,6}; const int even[4]={8,4,2,6}; int div2(int a, int c) // a/(2^c) { int i; for(i=0; i<4 && even[i]!=a; i++); if(i>=4) return 0; return even[(i+c)%4]; } int lastbit(char*s) { int len=strlen(s); if(len<2) return m[s[0]-'0']; int i, c, bit=1; for(i=len-1; i>=0; i--) b[len-1-i]=s[i]-'0'; while(len>1) { int k = b[0]; for(i=1,c=b[0]/5; i<=len; i++) // b=b/5(b=b*2/10) { c = b[i] * 2 + c; b[i-1] = c % 10; c /= 10; } len -= !b[len-1]; bit = bit * div2(table[k],b[0]+b[1]*10) % 10; } bit = bit * m[b[0]] % 10; return bit; } int main(void) { while(cin>>n) cout<<lastbit(n)<<endl; return 0; }