整除问题 - 九度教程第55题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LYKXHTP/article/details/88430444

整除问题 - 九度教程第55题

题目

时间限制:1秒 内存限制:32兆 特殊判题:否
题目描述:
给定n,a求最大的k,使n!可以被a^k整除但不能被a^(k+1)整除。
输入:
两个整数 n(2<=n<=1000),a(2<=a<=1000)
输出:
一个整数.
样例输入:
6 10
样例输出:
1
来源:
2011 年上海交通大学计算机研究生机试真题

n!和a^k可能数值非常巨大,而不能被int(甚至long long)保存,也就不能直接用求余操作判断它们是否存在整除关系。那么就得从整除的特征入手,转而思考若整数a能整除整数b则它们之间存在的关系。不妨对a和b分解素因数:
在这里插入图片描述
则,式b除以a能表示成:
在这里插入图片描述
若a能整除b,则该式为一个整数,但考虑到若素数p1能够整除素数p2,则p1必等于p2(两个素数必互质)。则可以得出如下规律:
若a存在素因数px则b也必存在该素因数,且该素因数在b中对应的幂指数必不小于在a中的幂指数。
设x=n!, y=a^k, 对n!与a分解素因数,令:
在这里插入图片描述
相应也可以得到a^k的素因数分解为:
在这里插入图片描述
即要确定最大的非负整数k,使a中任一素因数的幂指数的k倍依旧小于或等于该素因数在x中对应的幂指数。要求的该k只需依次测试a中每一个素因数,确定x中该素因数对应的幂指数是a中幂指数的几倍(利用整数除法),这样所有倍数中最小的那个即为要求的k。

剩余的工作就是对a和n!分解素因数。
由于n!数值非常巨大(当n>30时),想要计算n!后再类似a一样对其分解质因数不现实。考虑n!中含有素因数p的个数,即确定素因数p对应的幂指数。易知n!中包含了1到n区间内所有整数的乘积,这些乘积中每一个p的倍数(包括其本身)都将对n!贡献至少一个p因子,且知道在1到n中p的倍数共有n/p(整数除法)个,则p的因子数至少为n/p个,即有n/p个整数至少贡献了一个p因子。那么有多少个整数将贡献至少两个p因子呢,所有pp的倍数将为n!贡献至少2个p因子,且这样的整数有n/(pp)个;同理ppp的倍数将贡献至少3个p因子,这样的数有(n/(ppp))个。

分解n!的质因数过程:
1.计算器清零,该计数器表示n!中将有几个p因子,即n!分解质因数后质因子p对应的幂指数。
2.计算n/p,有n/p个整数可以向n!提供一个p因子,则计数器累加n/p。若n/p为0,表示没有一个整数能向n!提供一个或一个以上的p因子,分解结束。
3.计算n/(pp),有n/(pp)个整数可以向n!提供两个p因子,但它们在之前步骤中(p的倍数必包括pp的倍数)每个数都已经向计算器累加了1个p因子,所以此处它们还能够向计数器贡献n/(pp)个素因子(即每个再贡献一个),累加器累加n/(pp)。若n/(pp)为0,表示没有一个整数能向n!提供两个或两个以上的p因子,分解结束。
4.计算n/(ppp),有n/(ppp)个整数可以向n!提供三个p因子,但它们在之前步骤中每个数已经向计数器累加了2个p因子,所以此处还能贡献n/(ppp)个素因子,累加器累加n/(ppp)。若n/(ppp)为0,表示没有一个整数能向n!提供三个或三个以上的p因子,分解结束。

依次累加p的更高次的倍数能够再提供的素因子数,即每次向计数器累加n/(p^k),直到n/(p^k)变为0,表示没有整数能提供更多的p因子,关于p的分解结束。
完成这些步骤后就能计算出n!中所有p的因子数,即计数器中累加的结果即为素因数p的幂指数。
有了对n!分解素因数的方法,只需依次遍历可能成为其素因子(小于等于n的所有素数)的素数,计算它们所对应的幂指数,即可完成对n!的素因数分解。

#include <stdio.h>
#include <string.h>

bool mark[1001];
int prime[1001];
int primeSize;

void init(){//筛选出0到1000范围内的所有素数
    primeSize=0;
    for(int i=2;i<=1000;i++){
        if(mark[i]==true)continue;
        mark[i]=true;
        prime[primeSize++]=i;
        for(int j=i*i;j<=1000;j+=i){
            mark[j]=true;
        }
    }
}

int cnt[1001];
//cnt[i]用来表示prime[i]所保存的素数在n!中的因子数
//即!分解素因数后素因子prime[i]所对应的幂指数,可能为0

int cnt2[1001];
//cnt2[i]用来表示prime[i]所保存的素数在a中的因子数

int main()
{
    init();
    int n,a;
    while(scanf("%d%d",&n,&a)==2){//成功读取的个数
        for(int i=0;i<primeSize;i++){
            cnt[i]=0;
            cnt2[i]=0;
            //将两个计数器清零,为新的一次分解做准备
        }
        for(int i=0;i<primeSize;i++){
            //对n!分解质因数,遍历0到1000的每个素数
            int t=n;//用临时变量t保存n的值
            while(t){
                cnt[i]+=t/prime[i];
                t=t/prime[i];
            }//依次计算t/(prime[i]^k),累加其值,直到t/(prime[i]^k)变为0
        }
        int ans=123123123;//答案初始值为一个大整数,为取最小值做准备
        for(int i=0;i<primeSize;i++){
            //对a分解素因数
            while(a%prime[i]==0){
                cnt2[i]++;
                a/=prime[i];
            }//计算a中素因数prime[i]对应的幂指数
            if(cnt2[i]==0)continue;
            //若该素数不能从a中分解到,即其对应幂指数为0
            //则其不影响整除性,跳过

            if(cnt[i]/cnt2[i]<ans){
                //计算素数prime[i]在两个数中因子数的商
                ans=cnt[i]/cnt2[i];//统计这些商的最小值
            }
        }
        printf("%d",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/LYKXHTP/article/details/88430444