zoukankan      html  css  js  c++  java
  • Noip2009 Hankson的趣味题

    Description

    Hanks 博士是BT (Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫Hankson。现在,刚刚放学回家的Hankson 正在思考一个有趣的问题。
    今天在课堂上,老师讲解了如何求两个正整数(c_1)(c_2) 的最大公约数和最小公倍数。现在Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数(a_0),(a_1),(b_0),(b_1),设某未知正整数x 满足:
    1.(x)(a_0) 的最大公约数是(a_1)
    2.(x)(b_0) 的最小公倍数是(b_1)
    Hankson 的“逆问题”就是求出满足条件的正整数x。但稍加思索之后,他发现这样的(x) 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的(x) 的个数。请你帮助他编程求解这个问题。

    Input

    第一行为一个正整数(n),表示有(n) 组输入数据。接下来的n 行每行一组输入数据,为四个正整数(a_0)(a_1)(b_0)(b_1),每两个整数之间用一个空格隔开。输入数据保证(a_0) 能被(a_1) 整除,(b1) 能被(b_0) 整除。

    Output

    每组输入数据的输出结果占一行,为一个整数。
    对于每组数据:若不存在这样的 (x),请输出0;
    若存在这样的 (x),请输出满足条件的(x)的个数;

    Sample Input

    2 
    41 1 96 288 
    95 1 37 1776 
    

    Sample Output

    6
    2
    

    Hint

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

    题解

    90分算法

    我们把题目的内容表示一下

    [left{ egin{align*} gcd(a_0,x)&=a_1\ lcm(b_0,x)&=b_1\ end{align*} ight. ]

    显然,x一定是(b_1)的因数,一定是(a_1)的倍数。假设m表示(a_0)(a_1)(b_0)(b_1)的大小,我们可以(O(sqrt m))枚举(b_1)的约数x,然后判断x满不满足上面的方程组即可。由于gcd和lcm的时间复杂度都是(O(log m)),有(n)次询问,所以总时间复杂度为(O(n sqrt m log m))

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    
    template<typename A>inline void read(A&a){a=0;A f=1;int c=0;while(c<'0'||c>'9'){c=getchar();if(c=='-')f*=-1;}while(c>='0'&&c<='9'){a=a*10+c-'0';c=getchar();}a*=f;}
    template<typename A,typename B>inline void read(A&a,B&b){read(a);read(b);}
    template<typename A,typename B,typename C>inline void read(A&a,B&b,C&c){read(a);read(b);read(c);}
    
    typedef long long ll;
    
    int T;
    int a0,a1,b0,b1,ans;
    
    inline int gcd(int x,int y){return x%y==0?y:gcd(y,x%y);}
    inline int lcm(int x,int y){return x/gcd(x,y)*y;}
    inline bool check(int x){return gcd(x,a0)==a1&&lcm(x,b0)==b1;}
    
    void Init(){
        read(a0,a1);
        read(b0,b1);
        ans=0;
    }
    
    void Work(){
        int t=sqrt(b1);
        for(register int i=1;i<=t;++i){
            if(!(b1%i)){
                if(check(i))++ans;
                if(b1/i!=i)if(check(b1/i))++ans;
            }
        }
        printf("%d
    ",ans);
    }
    
    int main(){
        read(T);
        while(T--){
            Init();
            Work();
        }
        return 0;
    } //鉴于这个代码是我一年前写的,所以如果码风不大正常还请见谅。
    

    这个算法在当年Noip的时候可以拿90分,但是在洛谷神机和codevs里面就可以过了。

    100分做法

    我们把上面的式子再放一遍。

    [left{ egin{align*} gcd(a_0,x)&=a_1\ lcm(b_0,x)&=b_1\ end{align*} ight. ]

    因为(x)(b_1)的约数,所以x的质因子一定是(b_1)拥有的。所以我们可以对于p的每一个质因子,计算x可以拥有多少个。
    我们枚举(b_1)的质因数p,设(a_0)(a_1)(b_0)(b_1)的分别有(m_{a_0})(m_{a_1})(m_{b_0})(m_{b_1})个,x有(m_x)个。
    对于(gcd(a_0,x)=a_1)这个式子,我们有三种情况:

    [left{ egin{align*} m_{a_0} > m_{a_1} quad &Rightarrow quad m_x = m_{a_1}\ m_{a_0} = m_{a_1} quad &Rightarrow quad m_x geq m_{a_1}\ m_{a_0} < m_{a_1} quad &Rightarrow quad ext{无解}\ end{align*} ight. ]

    对于(lcm(b_0,x)=b_1)这个式子,我们也有如下三种情况:

    [left{ egin{align*} m_{b_0} > m_{b_1} quad &Rightarrow quad ext{无解}\ m_{b_0} = m_{b_1} quad &Rightarrow quad m_x leq m_{b_1}\ m_{b_0} < m_{b_1} quad &Rightarrow quad m _x = m_{b_1}\ end{align*} ight. ]

    那么我们现在把上面集中情况整合一下:有如下几种情况:

    [left{ egin{align*} m_{a_0} > m_{a_1} , m_{b_0} < m_{b_1} , m_{a_1} = m_{b_1} qquad &Rightarrow qquad m_x ext{有$1$中取法}\ m_{a_0} > m_{a_1} , m_{b_0} = m_{b_1} , m_{a_1} leq m_{b_0} qquad &Rightarrow qquad m_x ext{有$1$中取法}\ m_{a_0} = m_{a_1} , m_{b_0} < m_{b_1} , m_{a_0} leq m_{b_1} qquad &Rightarrow qquad m_x ext{有$1$中取法}\ m_{a_0} = m_{a_1} , m_{b_0} = m_{b_1} , m_{a_1} leq m_{b_1} qquad &Rightarrow qquad m_x ext{有$m_{b_1}-m_{a_1}+1$中取法}\ ext{其他情况} qquad &Rightarrow qquad ext{无解}\ end{align*} ight. ]

    我们把(cnt_p)表示(m_x)的取值个数,(无解即为0),那么我们只要求出

    [prod limits_{ ext{质数}p|d}{} cnt_p ]

    即可。

    我们可以先用线性筛筛出(1..sqrt{2 imes 10^9})的质数,然后对于每一次询问,枚举每个质数,判断能否整除(b_1),如果能整除,那么我们就按照上面的标准计算并累乘(cnt_p)。最后如果(b_1 eq 1),那么我们在对剩下来这个质数(b_1)再计算一次。
    由于最小的10个质数(2,3,5,7,11,13,17,19,23,29,31)的乘积已经超过(2 imes 10^9),所以能整除(b_1)的质数不会超过10个。由于在(1..d)的范围内有(frac {d}{ln d})个,所以该算法的时间复杂度为(O(frac{nsqrt{m}}{ln sqrt m}))。这样就不会超时了,该算法可以获得100分。

    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    using namespace std;
    
    const int maxn=2000+7,maxm=2e9;
    int n,a0,a1,b0,b1,ans,p[45000],np[45000],tot;
    
    inline void Prime(){
    	np[0]=np[1]=1;int t=sqrt(maxm);
    	for(register int i=2;i<=t;++i){
    		if(!np[i])p[++tot]=i;
    		for(register int j=1;j<=tot&&i*p[j]<=t;++j){
    			np[i*p[j]]=1;
    			if(i%p[j]==0)break;
    		}
    	}
    }
    
    void Process(int p){
    	int a0m=0,a1m=0,b0m=0,b1m=0;
    	while(a0%p==0)++a0m,a0/=p;
    	while(a1%p==0)++a1m,a1/=p;
    	while(b0%p==0)++b0m,b0/=p;
    	while(b1%p==0)++b1m,b1/=p;
    	if(a0m==a1m&&b0m==b1m&&a1m<=b1m)ans*=(b1m-a1m+1);
    	else if((a0m>a1m&&b0m<b1m&&a1m==b1m)||(a0m>a1m&&b0m==b1m&&a1m<=b0m)||(a0m==a1m&&b0m<b1m&&a0m<=b1m))return;
    	else ans=0;
    }
    
    int main(){
    	scanf("%d",&n);Prime();
    	while(n--){
    		scanf("%d%d%d%d",&a0,&a1,&b0,&b1);ans=1;
    		for(register int i=1;i<=tot;++i){
    			if(b1%p[i]==0)Process(p[i]);
    			if(!ans)break;
    		}
    		if(ans&&b1!=1)Process(b1);
    		printf("%d
    ",ans);
    	}
    }
    
  • 相关阅读:
    改变form里面input,textarea.select等的默认样式
    serialize() 方法
    纯css实现互动清单
    less学习笔记
    竖直手风琴导航菜单栏
    植树节快到了-那就种棵决策树吧
    简单写个logictic回归
    爬取纽约时报特定关键词新闻并计数
    (数学建模)非线性规划
    (CV学习笔记)看图说话(Image Captioning)-2
  • 原文地址:https://www.cnblogs.com/hankeke/p/Noip2009-Hankson.html
Copyright © 2011-2022 走看看