POJ 1150-The Last Non-zero Digit
来源:互联网 发布:淘宝网代理一件代发 编辑:程序博客网 时间:2024/05/18 04:01
题意:
求排列数n,m的最后一个非零数字。
下面是超时程序:
#include<iostream>#include<cstring>#include<string>#include<algorithm>#include<cstdio>#include<cmath>using namespace std;int fun(int a, int b, int c) //a的b次方对c取模{if(b == 0) return 1;if(b == 1) return a % c;long long tmp = fun(a, b / 2, c);return (((tmp * tmp % c) * (b & 1 ? a : 1)) % c) % c;}int get2(int n) //n的阶乘中2的个数{int count = 0;while(n /= 2)count += n;return count;}int get5(int n) //n的阶乘中5的个数{int count = 0;while(n /= 5)count += n;return count;}int main(){//freopen("Input.txt", "r", stdin);int n, m, sum2, sum5, res, sum;while(scanf("%d%d", &n, &m) != EOF){sum2 = get2(n) - get2(n - m);sum5 = get5(n) - get5(n - m);res = (sum2 >= sum5 ? sum2 - sum5 : sum5 - sum2); //2的个数和5的个数的差值sum = 1;for(int i = n; i >= n - m + 1; --i){sum *= i;while(sum % 2 == 0) sum /= 2;while(sum % 5 == 0) sum /= 5;sum %= 10;}if(sum2 >= sum5) //加上2或5对结果的影响sum *= fun(2, res, 10);elsesum *= fun(5, res, 10);printf("%d\n", sum % 10);}return 0;}
看过之后才明白有规律,优化后代码:
/*如何求出n!阶乘最后非0位?比如说我们要找10!最后非0位,由于质因数2和5组合之后会使得末尾产生0.那么我们不妨把10!中2,5质因数全部去掉,(但是请注意2的数目其实比5的要多,所以我们要在最后考虑多余的2对末位的影响)如 1*2*3*4*5*6*7*8*9*10 去掉2 ,5 因子后 就是1*1*3*1*1*3*7*1*9*1,由于2,5因子已经被去除,那么剩下的数字末尾一定是3,7,9,1中四者之一。然后我们再求出这么一串数相乘以后末尾的数是几.最后再补上2对末位的影响即可!总结一下,求10!最后一个非0位的步骤如下:step1:首先将10!中所有2,5因子去掉;step2:然后求出剩下的一串数字相乘后末尾的那个数。step3:由于去掉的2比5多,最后还要考虑多余的那部分2对结果的影响。step4:output your answer!正如上面文章里所说的“To compute the number of 3,7,9 among (f(1) mod 10), (f(2) mod 10), ..., (f(N) mod 10) is not so easy”,这里面步骤2是个难点。如何求出剩下的这串数字相乘后最后一位是几呢?这可以转化成求出这些数里面末尾是3,7,9的数字出现的次数(为啥?因为这些数的n次方是有规律的,周期为4,不信你可以推一下)好,现在问题就是如何求出这串数字中末尾3,7,9各自出现的次数了;一个数列实际上可以分成偶数列和奇数列,以1*2*3*4*5*6*7*8*9*10为例分成1 3 5 7 9, 2 4 6 8 10这样我们尝试分别进行统计,可以发现,实际上2,4,6,8,10中的个数也就是1 2 3 4 5中的个数,也就是说我们又把这个问题划分成了一个原来问题的子问题。f(n) = f(n/2) + g(n),g(n)表示奇数列中的数目,所以我们需要解决g(n)再次观察g(n)实际上又分成了两部分1 3 7 9 11 13 17 19 21。。。以及5的奇倍数5,15,25。。。说明又出现了子问题,如果要统计这个数列中末尾为x(1,3,7,9)的个数可以这样写:g(n,x) = n/10+(n%10 >= x)+g(n/5,x) 这样利用了两个递归方程,我们就可以在lgn的时间内计算出末尾为1,3,7,9的数的个数了好了,现在我们得到了这串数字中末尾是3,7,9的数字的个数,我们利用循环节的性质可以快速地算出这串数字相乘后mod 10的结果,在考虑下当时多除的2(其实也可以用循环节来处理),便可求出答案!解决了上面两个子问题,我想求P(n,m)最后一个非0位就变得十分容易了。P(n,m)实际上等于 n! / (n-m)!我们可以求出n! 和(n-m)!中质因数2,5,3,7,9分别出现的次数,然后再各自相减。然后再用循环节处理,即可!BTW,这里还要注意一个trick,就是2的出现次数如果小于5,(这对于排列数来说是可能的)我们可以直接输出5,如果2的数目等于5,那么2的循环节不需要考虑。至于3,7,9的循环节,由于这些数的4次方末位刚好是1,所以就不需要特殊考虑了。*/#include<cstdio>int getx(int n,int x){ if(n==0) return 0; return n/x+getx(n/x,x);}int g(int n,int x){ if(n==0) return 0; return n/10+(n%10>=x)+g(n/5,x);}int get(int n,int x){ if(n==0) return 0; return get(n/2,x)+g(n,x);}int table[4][4] ={ 6,2,4,8,//2 ^n%10的循环节,注意如果2的个数为0时候,结果应该是1,要特殊处理。 1,3,9,7,//3 1,7,9,3,//7 1,9,1,9,//9};//3,7,9的循环节中第一位,刚好是1,故不需要考虑这些数字出现次数为0的情况。int main(){ int n,m; int num2,num3,num5,num7,num9; while(scanf("%d%d",&n,&m)!=EOF)//n!/(n-m)!的右边第一个非0位 { num2=getx(n,2)-getx(n-m,2); num3=get(n,3)-get(n-m,3); num5=getx(n,5)-getx(n-m,5); num7=get(n,7)-get(n-m,7); num9=get(n,9)-get(n-m,9); int res=1; if(num5>num2) { printf("5/n"); continue; } else { if(num2!=num5) { res*=table[0][(num2-num5)%4]; res=res%10; } res*=table[1][num3%4]; res%=10; res*=table[2][num7%4]; res%=10; res*=table[3][num9%4]; res%=10; } printf("%d\n",res); } return 0;}
- poj 1150 The Last Non-zero Digit
- poj 1150 The Last Non-zero Digit
- POJ 1150-The Last Non-zero Digit
- poj 1150 The Last Non-zero Digit
- poj 1150 The Last Non-zero Digit
- POJ 1150 The Last Non-zero Digit
- poj 1150 The Last Non-zero Digit
- POJ 1150 The Last Non-zero Digit
- POJ 1150 The Last Non-zero Digit 数论+容斥
- POJ 1150 The Last Non-zero Digit 已翻译
- PKU 1150 The Last Non-zero Digit
- The Last Non-zero Digit
- POJ 1150 The Last Non-zero Digit 阶乘最后非0位
- 排列数最后非零位:poj 1150 The Last Non-zero Digit
- POJ 1150-The Last Non-zero Digit(求阶乘最后一位非零数)
- poj 1150 The Last Non-zero Digit 求排列数的最后非零数
- PKU 1055 The Last Non-zero Digit
- POJ1150 The Last Non-zero Digit
- 编译/安装Wireshark
- C# 中复制结构变量的条件及方法
- Java软件低级错误(十 六):使用包装器对象带来的低效问题
- PB 过期解决方法
- Mac配置docbook工具链
- POJ 1150-The Last Non-zero Digit
- Linux 系统启动流程
- JTable中SelectionModel的一個例子
- Java软件低级错误(十 九):静态变量的初始化问题
- Java软件低级错误(十 一):正确理解Java的浅clone和深clone
- C++学习序列笔记之内联函数
- C++学习序列笔记之函数原型和函数定义
- JRTPLIB 3.5.2手册中文版---part5
- Could not append.Requesting close of hlog