题意:给出n,m,求n中取m个的排列数抛掉末尾的0后的最后一位。
分析:题目可以转化为求一段连续数列的乘积的最后非0位。乘积的末尾之所以会有0,是因为数字中有包含2和5的倍数,2和5这两个质因子组成了0。我们先将所有2和5质因子去除,求其余数字乘积的最小位。我们将2的倍数全部取出,把每个数字除以2,这样就得到了一个小一些的连续数列,这样就转化为了一个子问题,用相同方法求其最小非0位即可,具体方法稍后讲。原连续数列中去除2的倍数的同时5的偶数倍也被去除了,还剩下所有的奇数,对于这个奇数数列我们将5的奇数倍(以5结尾的数字)取出,对于这些数字除以5之后得到了一个较小的连续奇数列,转化为了一个子问题。此时原连续数列中还剩下以1,3,7,9结尾的数字,这些数字用找循环节的方式求乘积最小位即可。到目前为止,对于连续数列的求解被我们分为3部分,一部分直接计算(1379结尾的),另外两部分转化为子问题,一个子问题是求连续数列的乘积最小非0位(2的倍数),另一个子问题是求连续奇数列的最小非0位(以5结尾的)。对于奇数列的求解,我们把数列分为3部分,一部分直接求解(1379结尾的),另一部分转化为子问题(以5结尾的)。这样递归下去即可求出原连续数列乘积抛掉质因子2和5之后的最小位。我们在递归过程中可以记录被去掉的2和5的个数。现在我们试着将这些2和5乘回去。如果5比2多,那么原数列最小非0位是5。如果2比5多,那么相当于除去2和5的乘积最小位乘以比5多出来的那些2,同样可以用找循环节的方式求解。
View Code
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> using namespace std; int s, e; int count_two, count_five; int get_digit(int s, int e) { int s1 = s / 10 * 10 + 10; int e1 = e / 10 * 10; int ret = 1; if (e1 <= s1) { for (int i = s; i <= e; i++) if ((i & 1) && (i % 5 != 0)) ret = ret * (i % 10) % 10; return ret; } ret = (e1 - s1) / 10 % 2 * 8 + 1; for (int i = s; i < s1; i++) if ((i & 1) && (i % 5 != 0)) ret = ret * (i % 10) % 10; for (int i = e1 + 1; i <= e; i++) if ((i & 1) && (i % 5 != 0)) ret = ret * (i % 10) % 10; return ret; } int cal_odd(int s, int e) { if (s > e) return 1; int ret = get_digit(s, e); int s1 = s; while (s1 % 10 != 5) s1++; int e1 = e; while (e1 > 0 && e1 % 10 != 5) e1--; if (e1 < s1) return ret; s1 /= 5; e1 /= 5; ret = ret * cal_odd(s1, e1) % 10; count_five += (e1 - s1) / 2 + 1; return ret; } int cal(int s, int e) { if (s > e) return 1; int ret = cal((s + 1) / 2, e / 2) % 10; count_two += e / 2 - (s + 1) / 2 + 1; ret = ret * cal_odd(s, e) % 10; return ret; } void test(int s, int e) { long long a = 1; for (int i = s; i <= e; i++) { a *= i; while (a % 10 == 0) a /= 10; } printf("%lld", a % 10); } int main() { //freopen("t.txt", "r", stdin); while (~scanf("%d%d", &e, &s)) { s = e - s + 1; // test(s, e); count_two = 0; count_five = 0; int ans = cal(s, e); if (count_five > count_two) { puts("5"); continue; } int temp = count_two - count_five; if (temp == 0) { printf("%d\n", ans); continue; } temp--; ans = ans * 2 % 10; for (int i = 0; i < temp % 4; i++) ans = ans * 2 % 10; printf("%d\n", ans); } return 0; }