整除问题

题目描述

  给定n,a求最大的k,使n!可以被a^k整除但不能被a^(k+1)整除。

输入

  两个整数n(2<=n<=1000),a(2<=a<=1000)

输出

  一个整数

样例输入

  6 10

样例输出

  1

题目来源:上交计算机研究生机试真题


分析一下:首先从n和a的取值范围可以知道,n!和a^k的数值可能是非常大的,甚至会超出int,long long的范围,所以直接用求余数操作判断它们是否存在整除关系的方法可以被排除了。

可以这样思考,如果整数a能整除整数b,则满足下面的关系

其中pi表示的素数,ei表示的是幂指数。

也就是说我们可以将a和b分解成素因数乘积的形式,若a中存在素因数p则b也必存在该素因数,且该素因数在b中对应的幂指数必不小于在a中的幂指数。

按照这个思路,我们就可以求出k值,即使a中任意苏因素的幂指数的k倍小于或者等于该素因数在n!中对应的幂指数。所以可以采用遍历b和a中对应幂指数商的方法,获得最小值k即可。

对于n!分解素因数,只需一次遍历可能成为其素因子(小于等于n的所有素数)的素数,计算它们对应的幂指数,即可完成对n!的素因数分解。

对于素数的判定采用的是,素数筛选法,前面文章有介绍。

int prime[1000];
bool flag[1001];

int size = 0;
void intPrime()
{
    for (int i = 0; i < 1001; i++)
        flag[i] = true;
    for (int i = 2; i <= 1000; i++)
    {
        if (flag[i] == false)
            continue;
        else
        {
            prime[size++] = i;
            for (int j = i*i; j <= 1000; j +=i)
                flag[j] = false;
        }
    }
}
View Code

下面给出两种方法:

//n!中素因数的个数
int countsOfPrime1[1000];
//a中素因数的个数
int countsOfPrime2[1000];

方法一:

void main(void)
{
    int n, a;
    intPrime();
    while (cin >> n >> a)
    {
        memset(countsOfPrime1, 0, 1000);
        memset(countsOfPrime2, 0, 1000);
        while (n != 1)
        {
            int size = 0;
            int t = n;
            for (int i = 0; i < size; i++)
            {
                while (t % prime[i] == 0)
                {
                    countsOfPrime1[i] ++;
                    t = t / prime[i];
                }
                if (t == 1)
                    break;
            }
            n--;
        }
        int ans = 999999;
        for (int i = 0; i < size; i++)
        {
            while (a % prime[i] == 0)
            {
                countsOfPrime2[i]++;
                a /= prime[i];
            }

            if (countsOfPrime2[i] == 0)
                continue;
            if (countsOfPrime1[i] / countsOfPrime2[i] < ans)
                ans = countsOfPrime1[i] / countsOfPrime2[i];
            if (a == 1)
                break;
        }

        cout << ans;
    }
}
View Code

方法二:

void main(void)
{
    int n, a;
    intPrime();
    while (cin >> n >> a)
    {
        memset(countsOfPrime1, 0, 1000);
        memset(countsOfPrime2, 0, 1000);
        for (int i = 0; i < size; i++)
        {
            int t = n;
            while (t)
            {
                countsOfPrime1[i] += t/prime[i];
                t = t / prime[i];
            }

        }

        int ans = 999999;
        for (int i = 0; i < size; i++)
        {
            while (a % prime[i] == 0)
            {
                countsOfPrime2[i]++;
                a /= prime[i];
            }

            if (countsOfPrime2[i] == 0)
                continue;
            if (countsOfPrime1[i] / countsOfPrime2[i] < ans)
                ans = countsOfPrime1[i] / countsOfPrime2[i];
        }

        cout << ans;
    }
}
View Code

方法二相对于方法一时间效率上更加高效,因为它没有依次遍历1...n之间每个数的素因数个数,而是一次遍历求得可能成为其素因子的个数。这样就少了一层循环,降低了一个数量级。

关于上面的算法,其实还可以再一次进行优化,这样就需要考虑一下在n和a的范围内求素因子即可。不难,有心者可以实现下。

原文地址:https://www.cnblogs.com/tgycoder/p/5007639.html