zoukankan      html  css  js  c++  java
  • P1072 Hankson 的趣味题[数论]

    题目描述

    Hanks 博士是 BT(Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。

    今天在课堂上,老师讲解了如何求两个正整数c_1c1 和 c_2c2 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a_0,a_1,b_0,b_1a0,a1,b0,b1,设某未知正整数xx 满足:

    1. xx 和 a_0a0 的最大公约数是 a_1a1;

    2. xx 和 b_0b0 的最小公倍数是b_1b1。

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

    解析

    这道题很容易想出一个比较暴力但是可以AC的解法。

    根据题目,我们有(gcd(a_0,x)=a_1,lcm(x,b_0)=b_1),那么显然(a_1mid x,xmid b_1),于是我们找出(b_1)的所有质因子,然后验证一下(a_1mid x)是否成立即可。注意在找质因子的时候不要打暴力,肯定会T,应先筛个素数,再用素数凑出(b_1),再判断,卡卡常随便过。

    如果是在考场上,这种做法无疑是最优的,好想又好写。但是实际上这道题有更好的巧解做法。


    (lcm(x,b_0)=b_1)(xmid b_1),故一定存在一个(k),使得(kmid x ~&~ kmid b_1),即(x)的质因子一定是(b_1)的质因子。

    由此我们可以想到,我们不妨枚举所有(1sim sqrt{(2*10^9)})的所有素数(k),并不断将质因子(k)(a_0,a_1,b_0,b_1)中除去,同时我们可以通过每个数含有(k)的数量确定一些可行的答案。如果最终(b_1 ot= 1),根据素数理论,说明(b_1)本身就是质数。

    那么如何通过四个数所含有的(k)的数量来判断解的情况呢?

    (a_0,a_1,b_0,b_1)分别含有(ma,mb,mc,md)个质因子(k)(x)含有(mx)个质因子(k),那么我们可以进行如下讨论(可以类比朴素解法的检验过程):

    对于(gcd(x,a_0)=a_1)这一约束条件:

    1. (ma>mb)时,有一个解(mx=mb)
    2. (ma=mb)时,有解(mx>=mb)
    3. (ma<mb)时,无解。

    对于(lcm(x,b_0)=b_1)这一约束条件:

    1. (mc<md)时,有一个解(mx=md)
    2. (mc=md)时,有解(mx<=md)
    3. (mc>md)时,无解。

    故我们可以发现,只要(ma<mb~||~mc>md),我们就可以排除当前质因子(k)存在解的可能性。而当(mc=md,ma=mb,mc<=md)时,有(md-mb+1)个解。

    要明确一点,我们每次求解的方案数是在质因子(k)意义下的解,即(x)包含质因子(k)的方案数(有点难理解,好好思考)。最终的某个解(x’)一定是其中一些符合要求的质因子相乘得到的,因此根据乘法原理,对于每种符合要求的质因子(k)能取的方案数,我们累乘到答案中,即可的到所有可行质因子(k)最终构成所有(x)的数量。(仔细理解质因子(k)与答案(x)的关系)

    设对于质因子(k)(x)可能包含它的方案数为(cnt_k),则最终答案可以表示为:

    [prod_{k~mid b_1}~cnt_k ]

    该算法十分高效,却极其难想出来,可谓一种毒瘤解法。。。

    参考代码

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<string>
    #include<cstdlib>
    #include<queue>
    #include<vector>
    #define INF 0x3f3f3f3f
    #define PI acos(-1.0)
    #define N 500010
    #define MAX 2000000000
    #define MOD 2520
    #define E 1e-12
    #define ll long long
    using namespace std;
    ll p[N],v[N],q,m;
    ll a0,a1,b0,b1,ans=1;
    inline void get(ll n)
    {
    	memset(v,0,sizeof(v));
    	m=0;
    	for(int i=2;i<=n;++i){
    		if(v[i]==0){v[i]=i;p[++m]=i;}
    		for(int j=1;j<=m;++j){
    			if(p[j]>n/i||p[j]>v[i]) break;
    			v[i*p[j]]=p[j];
    		}
    	}
    }
    inline void work(int p)
    {
    	ll ma=0,mb=0,mc=0,md=0;
    	while(!(a0%p)) a0/=p,ma++;
    	while(!(a1%p)) a1/=p,mc++;
    	while(!(b0%p)) b0/=p,mb++;
    	while(!(b1%p)) b1/=p,md++;
    	if(ma<mc||mb>md){ans=0;return;}
    	if(ma==mc&&mb==md){if(mc<=md)ans*=(md-mc+1);else ans=0;return;}
    	if(((ma>mc)&&(mb==md))||((ma==mc)&&(mb<md))){if(mc<=md)ans*=1;else ans=0;return;}
    	if((ma>mc)&&(mb<md)){if(mc==md)ans*=1;else ans=0;return;}
    }
    int main()
    {
    	scanf("%lld",&q);
    	get(sqrt(MAX));
    	while(q--){
    		ans=1;
    		scanf("%lld%lld%lld%lld",&a0,&a1,&b0,&b1);
    		for(int i=1;i<=m;++i)
    			work(p[i]);
    		if(a0>1) work(a0);
    		else if(b1>1&&b1!=a0)
    			work(b1);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    }
    

    总结

    对于以上两种算法,一点个人理解:暴力算法采用某种自顶向下的判断性求解模式,即判断当前枚举到的(x)是否符合条件。更优的算法采用一种类似构造解的模式,根据解的特征自底向上地构造解,应该说是一种较难掌握的思维方式。写完这道题,我想朴素与优雅的算法的区别应该就在这里吧。

  • 相关阅读:
    numpy 基础 —— np.linalg
    图像旋转后显示不完全
    opencv ---getRotationMatrix2D函数
    PS1--cannot be loaded because the execution of scripts is disabled on this system
    打开jnlp Faild to validate certificate, the application will not be executed.
    BATCH(BAT批处理命令语法)
    oracle vm virtualbox 如何让虚拟机可以上网
    merge 实现
    Windows batch,echo到文件不成功,只打印出ECHO is on.
    python2.7.6 , setuptools pip install, 报错:UnicodeDecodeError:'ascii' codec can't decode byte
  • 原文地址:https://www.cnblogs.com/DarkValkyrie/p/11477719.html
Copyright © 2011-2022 走看看