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;
}
posted @ 2012-11-14 14:08  金海峰  阅读(548)  评论(0编辑  收藏  举报