挑战程序竞赛系列(38):4.1模运算的世界(1)

来源:互联网 发布:淘宝拆车喇叭 编辑:程序博客网 时间:2024/06/15 01:03

挑战程序竞赛系列(38):4.1模运算的世界(1)

详细代码可以fork下Github上leetcode项目,不定期更新。

练习题如下:

  • POJ 1150: The Last Non-zero Digit

POJ 1150: The Last Non-zero Digit

总算刷到了登峰造极,看了一些关于模运算的相关知识,但还是云里雾里,索性就直接开题,该系列每题都开篇博文讲解吧,知识点比较多。

此题推荐博文:http://www.hankcs.com/program/algorithm/poj-1150-the-last-non-zero-digit.html

更详细的解法:http://duanple.blog.163.com/blog/static/7097176720081016113033592/

数论入门推荐书籍:《初等数论及其应用》.

思路:
进行质因数分解,分解2和5,因为只有2和5的乘积才能构成10。在一个序列中:

1 2 3 4 5 6 7 8 9 10因数2的个数永远大于因数5的个数

所以说,我们只需要求出多出来的2的个数即可,这些将会影响非零位数的最终结果。

2的质因数个数如何求解:

1 2 3 4 5 6 7 8 9 10可以分解为:1 3 5 7 9 | 2 4 6 8 10前半部分个数:0后半部分个数:5 + f{1,2,3,4,5}没错,除了个2之后,能够得到5个质因数该问题就变成了子问题{1,2,3,4,5}的质因数个数递归解决,复杂度O(logn)

代码如下:

    public int total2(int n){        if (n == 0) return 0;        return n / 2 + total2(n / 2);    }

质因数5的个数如何?和2一个道理,所以有:

    public int total5(int n){        if (n == 0) return 0;        return n / 5 + total5(n / 5);    }

所以综合下(递归版本):

    public int fact_prime(int n, int x){        if (n == 0) return 0;        return n / x + fact_prime(n / x, x);    }

迭代版本:

    public int fact_prime_iter(int n, int x){        int res = 0;        while (n > 0){            res += n / x;            n = n / x;        }        return res;    }

接着,把所有的2和5的质因数全部排除后,剩余了什么?

1 2 3 4 5 6 7 8 9 101 1 3 1 1 3 7 1 9 1只剩下{1,3,7,9}了,那么为了能够计算非零位数,统计这些1379的个数就完事了。统计蛮取巧,分成奇数列和偶数列1 3 5 7 9 | 2 4 6 8 10前半部分的个数分别为:1:2 3:1 7:1 9:1后半部分的个数分别为:子问题{1,2,3,4,5}子问题可以不去考虑了,前半部分怎么统计呢?观察发现它包含有5的奇数倍项,但这奇数倍均除以5得到1,3,5,7....所以变成了前部部分的子问题了...所以问题的关键就变成了求解1379各自的个数了其实都不需要求,直接看出来了当n = 101:1, 3:1, 7:1, 9:1当n = 131:2 3:2 7:1 9:1因为13中有1113个位数分别为13所以给定n我们有 g(n, x) = n / 10 + (n % 10 >= x) + g(n / 5, x);n/10: 每10个数都有一个n%19: n的个位数可能产生一个

好了,代码就出来了:

    public int odd_end_with(int n, int x){        if (n == 0) return 0;        int res = n / 10;        res += (n % 10 >= x ? 1 : 0);        return res + odd_end_with(n / 5, x);    }    public int end_with(int n, int x){        if (n == 0) return 0;        return end_with(n / 2, x) + odd_end_with(n, x);    }

最后如何计算N!(NM)!

很简单,因为N-M完全包含N,所以直接把统计结果减掉即可,最终代码如下:

import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import java.util.StringTokenizer;public class Main{    String INPUT = "./data/judge/201708/P1150.txt";    public static void main(String[] args) throws IOException {        new Main().run();    }    int[][] table = {            {6, 2, 4, 8},            {1, 3, 9, 7}, // 3              {1, 7, 9, 3}, // 7            {1, 9, 1, 9}, // 9    };    void solve() {        while (more()){            int N = ni();            int M = ni();            // 分解质因数 2 和 5 并且分别统计 2 和 5 的个数            int two    = fact_prime(N, 2) - fact_prime_iter(N - M, 2);            int five   = fact_prime(N, 5) - fact_prime_iter(N - M, 5);            int remain = two - five;            // 统计 1 3 7 9 的个数            int three  = end_with(N, 3) - end_with(N - M, 3);            int seven  = end_with(N, 7) - end_with(N - M, 7);            int nine   = end_with(N, 9) - end_with(N - M, 9);            int ans = 1;            ans *= remain == 0 ? 1 : table[0][remain % 4];            ans *= three  == 0 ? 1 : table[1][three  % 4];            ans *= seven  == 0 ? 1 : table[2][seven  % 4];            ans *= nine   == 0 ? 1 : table[3][nine   % 4];            ans %= 10;            out.println(ans);        }    }    public int fact_prime(int n, int x){        if (n == 0) return 0;        return n / x + fact_prime(n / x, x);    }    public int fact_prime_iter(int n, int x){        int res = 0;        while (n > 0){            res += n / x;            n = n / x;        }        return res;    }    public int odd_end_with(int n, int x){        if (n == 0) return 0;        int res = n / 10;        res += (n % 10 >= x ? 1 : 0);        return res + odd_end_with(n / 5, x);    }    public int end_with(int n, int x){        if (n == 0) return 0;        return end_with(n / 2, x) + odd_end_with(n, x);    }    FastScanner in;    PrintWriter out;    void run() throws IOException {        boolean oj;        try {            oj = ! System.getProperty("user.dir").equals("F:\\java_workspace\\leetcode");        } catch (Exception e) {            oj = System.getProperty("ONLINE_JUDGE") != null;        }        InputStream is = oj ? System.in : new FileInputStream(new File(INPUT));        in = new FastScanner(is);        out = new PrintWriter(System.out);        long s = System.currentTimeMillis();        solve();        out.flush();        if (!oj){            System.out.println("[" + (System.currentTimeMillis() - s) + "ms]");        }    }    public boolean more(){        return in.hasNext();    }    public int ni(){        return in.nextInt();    }    public long nl(){        return in.nextLong();    }    public double nd(){        return in.nextDouble();    }    public String ns(){        return in.nextString();    }    public char nc(){        return in.nextChar();    }    class FastScanner {        BufferedReader br;        StringTokenizer st;        boolean hasNext;        public FastScanner(InputStream is) throws IOException {            br = new BufferedReader(new InputStreamReader(is));            hasNext = true;        }        public String nextToken() {            while (st == null || !st.hasMoreTokens()) {                try {                    st = new StringTokenizer(br.readLine());                } catch (Exception e) {                    hasNext = false;                    return "##";                }            }            return st.nextToken();        }        String next = null;        public boolean hasNext(){            next = nextToken();            return hasNext;        }        public int nextInt() {            if (next == null){                hasNext();            }            String more = next;            next = null;            return Integer.parseInt(more);        }        public long nextLong() {            if (next == null){                hasNext();            }            String more = next;            next = null;            return Long.parseLong(more);        }        public double nextDouble() {            if (next == null){                hasNext();            }            String more = next;            next = null;            return Double.parseDouble(more);        }        public String nextString(){            if (next == null){                hasNext();            }            String more = next;            next = null;            return more;        }        public char nextChar(){            if (next == null){                hasNext();            }            String more = next;            next = null;            return more.charAt(0);        }    }}
阅读全文
0 0