阶乘问题 【数论】
题目来源
洛谷1134 https://www.luogu.org/problemnew/show/P1134
题目简述
求 N!中最右侧第一个非零项 (命名为有效位)
输入输出格式
输入格式:
仅一行包含一个正整数 NN 。
输出格式:
一个整数,表示最右边的非零位的值。
分析
对于任意 N>1
有效位只能为 2,4,6,8
观察这几个数之间的连系(我的着手点为2进制)
2=2
4=2x2
8=2x2x2
6=2x2x2x2 (有效位)
2=2x2x2x2x2
4=2x2x2x2x2x2
......
那么可以得到
在这四个数中,
任意数 x6 得到其本身
任意数 x8 等价于减少一个 x2 (2=2x2x2x2x2)
接着,对于所有存在的个位数字 (因为需要排除个位为零导致十位数字参与运算的干扰,排除数字5,0)
1x2x3x4x6x7x8x9=6 (72576有效位为6)
而根据前文分析 ,x6是只会无意义的
所以对于 10*(k+1)>N>10*k 时 (1~10*k) 的相乘是无意义的
即我们只需要计算 N的个位相乘
最后再看我们先前忽略的干扰数字:
0 无法带来任何改变 ,忽略
5 对于 2x2x2x..x2 的一个数来说 x5 等价于减少一个 x2 ,也就是说 x5 等价于 x8 (我们这么做的目的是为了排除x5所导致对十位数字的计算)。而因为1~N中因数5的系数是连续的(例如N=32 具有1x5,2x5,3x5,4x5,5x5,6x5 共6(32/5)个5需要特殊处理,且其中系数1,2,3,4,5,6是连续且需要计算的,运算过程直接当成 N=6算即可)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<queue> 2 #include<cstdio> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 int N; 8 void Read(int &x){ 9 x=0; 10 char c=getchar(); 11 while(!isdigit(c)) c=getchar(); 12 while(isdigit(c)) x=x*10-'0'+c,c=getchar(); 13 return ; 14 } 15 void Write(int x){ 16 int np=0; 17 int s[505]; 18 while(x) s[++np]=x%10,x/=10; 19 while(np) putchar(s[np--]+'0'); 20 putchar('\n'); 21 return ; 22 } 23 int E[4]={6,8,4,2}; //乘8的有效位(以4为周期的数列) 24 int main(){ 25 Read(N); 26 int ans=1; 27 while(N){ 28 for(int i=1;i<=(N%10);i++) 29 if(i!=5) ans=(ans*i)%10; 30 N/=5; //计算特殊处理的5 和 这些5的系数 31 ans=(ans*E[N%4])%10; 32 } 33 Write(ans); 34 return 0; 35 }