【NOIP 2009】Hankson的趣味题 分析&渣程序(增补对于大质数的分析)

来源:互联网 发布:初中教育状况网络调研 编辑:程序博客网 时间:2024/05/21 11:17

说实话我本来是懒得写题解的。。但是这道题实在是做得太蛋疼了。而且翻了翻网上又没有什么很详细的分析,所以就打算自己来写一篇= =先上题目。

题目描述

Hanks博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson正在思考一个有趣的问题。 今天在课堂上,老师讲解了如何求两个正整数c1和c2的最大公约数和最小公倍数。现在Hankson认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:

已知正整数a0,a1,b0,b1,设某未知正整数x满足:

1. x和a0的最大公约数是a1;

2. x和b0的最小公倍数是b1。

Hankson的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的 x并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的x的个数。请你帮助他编程求解这个问题。

输入

输入文件名为son.in。第一行为一个正整数n,表示有n组输入数据。接下来的n行每行一组输入数据,为四个正整数a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入数据保证a0能被a1整除,b1能被b0整除。

输出

输出文件名为son.out。输出共n行。每组输入数据的输出结果占一行,为一个整数。 对于每组数据:若不存在这样的x,请输出0; 若存在这样的x,请输出满足条件的x的个数;

样例输入

241 1 96 28895 1 37 1776

样例输出

62 

提示

说明】 第一组输入数据,x 可以是9、18、36、72、144、288,共有6 个。 第二组输入数据,x 可以是48、1776,共有2 个。 【数据范围】 对于 50%的数据,保证有1≤a0,a1,b0,b1≤10000 且n≤100。 对于 100%的数据,保证有1≤a0,a1,b0,b1≤2,000,000,000 且n≤2000。

这道题要用到一定程度的数论知识,接下来我们就先分析一下题目。

首先我们不难知道,对于某一个x,gcd(x/a1,a0/a1) = 1,gcd(b1/x , b1/b0) = 1。

在题目中,由于只是求出满足条件x的个数,因此,我们只需要明白x的构成有多少种方案即可。

由唯一分解定理可得,x必定是由若干个质数的若干次幂构成,即x = a1^b1 * a2^b2 *.....*an^bn

因此,我们只需要了解质数及它的次幂有多少种选择即可。

由于x是与a0,a1,b0,b1密切相关的,所以我们现在分解这四个数。

假设对于某个质因数p,a0,a1,b0,b1分别含有c_a0,c_a1,c_b0,c_b1个p。

那么,若c_a1 > c_a0 或 c_b0 > c_b1,则无解:

         由最大公约数和最小公倍数的性质可得:a0 % a1 = 0,b1 % b0 = 0。

         当a1和a0约去c_a0个p,b1和b0约去c_b1个p后,就会发现,此时a0 % a1 != 0,b1 % b0 != 0(a1和b0中多出了一些p无法被整除)

         因此无解。

若c_a1 < c_a0 或 c_b1 > c_b0,则x只能含有c_a1或c_b1个p,即只有一种选择:

         同样由上面的推导,由于x % a1 = 0,b1 % x = 0,若c_x > c_a1,而c_a0 > c_a1,则x和a0的最大公约数当然不只a1,起码是 a1 * p^min(c_x - c_a1,c_a0-c_a1)

         若c_x < c_a1 ,则x % a1就不可能为0。

         用同样的思想,我们可以论证c_b1 > c_b0时只有一种选择。

若c_a1 = c_a0 ,c_b1 = c_b0:

         对于前者,这便是c_x的下界。即x至少含有c_a1个p,用前面的方法可以很容易推出。

         同理,对于后者,这便是c_x的上界。

         因此,c_x的选择一共有(c_b1 - c_a1 + 1)种选择。

最后由乘法原理可得x总的选择数,即答案,下面贴代码(我的渣程序是1s多,应该可以优化。但由于TL是3s,于是过了= =)

#include <cstdio>#define MAXN 50000    //一个数的质因数不会超过它的根号,因此20E的范围开5W的质数表就足矣int prime[MAXN + 20];bool vis[MAXN + 20];int n,cnt,a0,a1,b0,b1,ans;void prime_form()                //筛法打出质数表{int i,j;for(i = 2;i <= MAXN;i++)if(!vis[i]){prime[++cnt] = i;for(j = 1;i*j <= 50000;j++)vis[i * j] = true;}}void work_one_step(int &a0,int &a1,int &b0,int &b1,int &result,int p){int cnt1 = 0,cnt2 = 0,cnt3 = 0,cnt4 = 0;   //判断a0,a1,b0,b1中质数p的指数while(a0 % p == 0) {cnt1++;a0/=p;}while(a1 % p == 0) {cnt2++;a1/=p;}while(b0 % p == 0) {cnt3++;b0/=p;}while(b1 % p == 0) {cnt4++;b1/=p;}/*gcd(a0/a1,x/a1) = 1,gcd(b1/x,b1/b0) = 1假设对于一个质因数p,a0,a1,b0,b1质因数分解后各有c_a0,c_a1,c_b0,c_b1个p,那么:        如果c_a0<c_a1,无解;如果c_a0=c_a1,x至少含有c_a1个p;如果c_a0>c_a1,那么x只可能含有c_a1个p。如果c_b1<c_b0,无解;如果c_b1=c_b0,x至多含有c_b1个p;如果c_b1>c_b0,那么x只可能含有c_b1个p。*/if(cnt1 == cnt2 &&  cnt3 == cnt4)   //c_a0=c_a1,c_b1=c_b0,在上界大于下界的情况下,可以选择的情况为上界减下界+1{if(cnt3 >= cnt1) result *= (cnt3 - cnt1 + 1);else result *= 0;}if(cnt1 > cnt2 && cnt3 == cnt4)   //c_a0>c_a1,c_b1=c_b0,此时x中只可能有c_a1个p,所以c_a1应在上界以内{if(cnt2 <= cnt3) result *= 1;else result *= 0;}if(cnt1 == cnt2 && cnt4 > cnt3)  //c_b1>c_b0,c_a0 = c_a1,此时x中只可能含有c_b1个p,所以c_b1要在下界以上{if(cnt4 >= cnt1) result *= 1;else result *= 0;}if(cnt1 > cnt2 && cnt4 > cnt3) //c_a0 > c_a1,c_b1 > c_b1,此时x既要满足只含c_a0个p,又要满足只含c_b1个p,所以c_a0 = c_b1{if(cnt2 == cnt4) result *= 1;else result *= 0;}}int work(int a0,int a1,int b0,int b1){if(a0 % a1 != 0 || b1 % b0 != 0) return 0;  //验证数据是否合法的剪枝int result = 1;for(int i = 1;i <= cnt;i++)work_one_step(a0,a1,b0,b1,result,prime[i]);if(a0 != 1) work_one_step(a0,a1,b0,b1,result,a0);   //处理超过5W的质数if(b1 != 1) work_one_step(a0,a1,b0,b1,result,b1);return result;}int main(){scanf("%d",&n);        prime_form();while(n--) {scanf("%d%d%d%d",&a0,&a1,&b0,&b1);printf("%d\n",work(a0,a1,b0,b1));}return 0;}

P.S渣分析和渣程序见谅=  =

增补(笔者极其蛋疼的理论分析,强烈建议略过):

              由于之前我的同学和我争论了很久关于除去5W以内的质因数后剩下的大质数处理问题,于是将结论整理后增补于此。

              我们设某个超过5W的大质数是P,所谓大质数的问题是指,假设a0 = P x  2,那么对于a0,在质数表的范围内是肯定无法找全质因数的,于是对于剩下的大质数应该特殊处理。

              在我之前的代码中,我用来处理大质数的代码是这样的:

      if(a0 != 1) work_one_step(a0,a1,b0,b1,result,a0);         if(b1 != 1) work_one_step(a0,a1,b0,b1,result,b1);
               我当时这样做是因为实在是太累了,于是就想偷个懒把所有情况处理了。当然这样做并不会出错,只是没有经过太多分析。

               事实上,这段代码可以是这样:

      if(a1 != 1) work_one_step(a0,a1,b0,b1,result,a1);         else if(b1 != 1) work_one_step(a0,a1,b0,b1,result,b1);
              或者是这样:

      if(a0 != 1) work_one_step(a0,a1,b0,b1,result,a0);         if(b1 != 1) work_one_step(a0,a1,b0,b1,result,b1);

              (其实就是原来的代码啦=  =,现在来加一点分析)

              在分析这两段代码前,我们先明确一个事实,即超过5W的大质数P,若有,便在每个数中仅有可能有一个

              并不难想,因为2个及以上的超过5W的数相乘必定大于20E。

              那么先来分析第一段代码:

             ①若a1不含此大质数,即a1最终为1,则此时进入下一if语句。判断b1时,若b1有一个大质数,那么在work_one_step的函数中就会对a0,a1,b0,b1这四个数进行大质数的除法,来保证此大质数影响最终结果的情况。若b1不含大质数,那么x当然也不可能含有这个大质数,而由于此时a1 = 1,所以a0是否含有大质数对于x并不影响。(即c_a1 = 0,c_a0 = 1 或 0,c_b1 = c_b0 = 0,因此c_x的上界为0,那么无论c_a0取1或0,c_x均只能取0,故只有一种选择)

             ②若a1含此大质数,即a1最终为P,那么x和a0中必然含有P,在第一个work_one_step中便会确定此大质数对于x的影响。

             值得一提的是,在②可能会有这样一种特殊情况:即b0含有另一个大质数_P,且b1也含有_P,那么根据之前的规则,x可以含有1个_P或者不含有_P。由于x中已经含有一个大质数P,因此不可能再含_P,即对于_P的选择只能为0。在a1与a0中当然也不可能含有_P,因此x含有_P的下界为0。于是x便只能含有0个_P。即选择只有一种,对最终答案并无影响。

              接下来是第二段代码:

              ①若a0不含此大质数,那么a1就更不可能含大质数。于是就会在下一if语句中判断b1是否含有大质数,来确定大质数对于最终答案的影响。(即对于b1中的大质数P,c_x的下界是0,上界由c_b0和b_b1决定)

              ②若a0含此大质数,那么就会判断a1,b0,b1是否含有此大质数来改变最终答案。在执行此操作后,需要再判断b1。因为b1有以下三种情况:

                              I.不含大质数:此时对于结果无影响。 

                              II.含有与a0相同的大质数P:在上个if语句中会被判断出来。

                              III.含有另一个大质数_P:倘若b0中同样含有这个大质数,那么对于x来说,c_x的上界是1。由于a0,a1不可能再含有_P,即c_a0 = c_a1 = 0,那么c_x的下界为0。此时此时c_x的选择便会是(1 - 0 + 1) = 2种,就会影响到最终结果,因此需要再判断。


P.S. 这一段完全是自己的纯理论分析,没有经过任何的数据验证。但是其实对于这道题的数据而言,有else和没有else都是可以过的。我只是无聊蛋疼的在钻牛角尖而已= =


0 0
原创粉丝点击