zoukankan      html  css  js  c++  java
  • poj 1150 The Last Nonzero Digit

    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就可以查询到一组数据了。

    代码如下:

    View Code
     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

  • 相关阅读:
    反射
    如何通过反射调用对象的方法?
    简述一下面向对象的”六原则一法则”。
    用Java写一个单例类。
    什么是UML?
    UML中有哪些常用的图?
    用Java写一个折半查找。
    两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对?
    构造器(constructor)是否可被重写(override)?
    用最有效率的方法计算2乘以8?
  • 原文地址:https://www.cnblogs.com/LyonLys/p/poj_1150_Lyon.html
Copyright © 2011-2022 走看看