http://poj.org/problem?id=1150
一道题意相当简单的数论题,求P(n,m)的最后一个非零位的数字,其中0≤m≤n≤2*10^7。直接暴力大数运算是会超时的,这里要用到一些数论的知识。
引用一下wyh师兄的解释:http://www.cppblog.com/wyh123/archive/2011/08/06/152621.html
排列公式:P(n,m)=n!/(n-m)!
将一个数2和5的因子都分离出来,最后这个数就会变成个位是1,3,7,9的数。其中,个位是1的数不会影响到最后一位非零数字。于是,我们就要统计那个区间内,将2和5两种因子的数去掉以后的那些数的最后一位是3,7,9的个数。统计的时候有个技巧,需要观察到,每十个数以内末数字是3,7,9在没有除去2和5两种因子前都只会出现一次。然后我打了个表出来观察,发现除去2和5的操作,实际上就是从原数列中抽出2和5的倍数来出以2和5。除了一次2或5以后,抽出来的序列又变成了从1~n/2或n/5的连续整数了。继续递归下去,从而统计出末数字是3,7,9的数在该区间中的个数。
不过分别处理除2和除5的时候,10的倍数的数会被除去两次,这时就要用容斥定理来补回10的倍数的数了,所以就得到下面的程序。
如果没有预处理小数据的情况,每次计算最多将递归计算10^6以上,但是如果把小数据先处理了,那么每次查询的时候就可以只用不到10^3就可以查询到一组数据了。
代码如下:
1 #include <cstdio> 2 3 const int maxn = 200000; 4 int tmp[maxn][10]; 5 6 void pre() { 7 for (int i = 1; i < maxn; i++) { 8 int t = i; 9 10 while (t % 2 == 0) t /= 2; 11 while (t % 5 == 0) t /= 5; 12 for (int j = 0; j < 10; j++) { 13 tmp[i][j] = tmp[i - 1][j]; 14 } 15 tmp[i][t % 10]++; 16 } 17 } 18 19 int cal(int n, int k) { 20 // printf("cal!!! %d %d\n", n, k); 21 if (n < maxn) return tmp[n][k]; 22 else return n / 10 + cal(n / 2, k) + cal(n / 5, k) + (n % 10 >= k) - cal(n / 10, k); 23 } 24 25 int deal(int n) { 26 int ret1 = 0, ret2 = 0; 27 int t = n; 28 29 while (t) { 30 ret1 += t >> 1; 31 t >>= 1; 32 } 33 while (n) { 34 ret2 += n / 5; 35 n /= 5; 36 } 37 // printf("ret1 %d ret2 %d\n", ret1, ret2); 38 // puts("!!!"); 39 40 return ret1 - ret2; 41 } 42 43 int main() { 44 int cnt3, cnt7, cnt9, cnt2; 45 int n, m; 46 47 pre(); 48 while (~scanf("%d%d", &n, &m)) { 49 if (m == 0){ 50 puts("1"); 51 continue; 52 } 53 54 // puts("~~~"); 55 m = n - m; 56 // printf("n %d m %d\n", n, m); 57 58 cnt3 = (cal(n, 3) - cal(m, 3)) & 3; 59 // puts("~~~1"); 60 cnt7 = (cal(n, 7) - cal(m, 7)) & 3; 61 // puts("~~~2"); 62 cnt9 = (cal(n, 9) - cal(m, 9)) & 1; 63 // puts("~~~~~~~"); 64 cnt2 = deal(n) - deal(m); 65 if (cnt2) cnt2 = (cnt2 - 1) % 4 + 1; 66 // printf("%d %d %d %d\n", cnt3, cnt7, cnt9, cnt2); 67 68 int ans = 1; 69 70 while (cnt3--) { 71 ans *= 3; 72 } 73 while (cnt7--) { 74 ans *= 7; 75 } 76 while (cnt9--) { 77 ans *= 9; 78 } 79 if (cnt2 > 0) { 80 while (cnt2--) { 81 ans <<= 1; 82 } 83 } else { 84 while (cnt2++) { 85 ans *= 5; 86 } 87 } 88 ans %= 10; 89 90 printf("%d\n", ans); 91 } 92 93 return 0; 94 }
——written by Lyon