poj1150

题意:给出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;

}

你可能感兴趣的:(poj)