洛谷P1072 Hankson 的趣味题(约数的√n/log(n)求法\gcd,lcm本质问题)

题意:传送门
题解:本题让求 g c d ( a , x ) = c 并 且 l c m ( b , x ) = d 的 x 的 个 数 gcd(a,x)=c并且lcm(b,x)=d的x的个数 gcd(a,x)=clcm(b,x)=dx,首先可以知道的 x x x一定是 d d d的约数,于是使用朴素算法的话,用试除法求出 d d d的所有约数,再判断下其余的两个条件,复杂度为 d + 2 ∗ l o g ( d ) \sqrt{d}+2*log(d) d +2log(d),总复杂度为 n ∗ d ≈ 1 e 8 n*\sqrt{d}\approx1e8 nd 1e8会超时,但是洛谷测试数据弱了,弱归弱,依旧要求正解不是,有个大概的公理需要了解下:

d的约数个数上限大约是√d,但是1~d中平均每个数的约数个数大约只有log(d)个。根据实际测试,10e9之内的自然数中,约数个数最多的自然数仅有1536个。

然后就是考虑如何快速求出一个数所有的约数呢?同时还有一个定理需要知道:

任何一个合数n必定包含一个不超过√n的质因子

所以先预处理处 1 ∼ 2 ∗ 1 0 9 1\sim\sqrt{2*10^9} 12109 的所有质数,然后进行质因数分解,最后搜索出所有的约数,复杂度为 O ( 2 ∗ 1 0 9 + l o g ( d ) O(\sqrt{2*10^9}+log(d) O(2109 +log(d)。之后直接再判断其余两个条件即可。
最后总的复杂度为: O ( 2 ∗ 1 0 9 + n ∗ √ d / l o g ( d ) + l o g ( d ) ∗ l o g 2 ( d ) O(\sqrt{2*10^9}+n*√d/log(d)+log(d)*log^2(d) O(2109 +nd/log(d)+log(d)log2(d)
c o d e : code: code:

#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
inline int read()
{
    
    
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int N=45000;
const int M=15;
int primes[N],cnt;
bool st[N];
pii factor[M];
int cntf;
int divider[N],cntd;
int n,a0,a1,b0,b1;
void get_primes(int n)
{
    
    
    for(int i=2;i<=n;i++){
    
    
        if(!st[i])primes[cnt++]=i;
        for(int j=0;j<cnt&&primes[j]*i<=n;j++){
    
    
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}
int gcd(int a,int b){
    
    return b==0?a:gcd(b,a%b);}
void dfs(int u,int p)
{
    
    
    if(u>cntf){
    
    
        divider[cntd++]=p;
        return;
    }
    for(int i=0;i<=factor[u].second;i++){
    
    
        dfs(u+1,p);
        p*=factor[u].first;
    }
}
int main()
{
    
    
    get_primes(50000);
    n=read();
    while(n--){
    
    
        a0=read();a1=read();b0=read();b1=read();
        int d=b1;
        cntf=0;
        for(int i=0;primes[i]<=d/primes[i];i++){
    
    
            int p=primes[i];
            if(d%p==0){
    
    
                int s=0;
                while(d%p==0)s++,d/=p;
                factor[++cntf]={
    
    p,s};
            }
        }
        if(d>1)factor[++cntf]={
    
    d,1};
        cntd=0;
        dfs(1,1);
        int res=0;
        for(int i=0;i<cntd;i++){
    
    
            int x=divider[i];
            if(gcd(x,a0)==a1&&(ll)x*b0/gcd(x,b0)==b1){
    
    
                res++;
            }
        }
        printf("%d\n",res);
    }
    return 0;
}

还有另外一种做法,是这样的,对于 g c d ( a , b ) = c gcd(a,b)=c gcd(a,b)=c而言
a = p 0 a 0 ∗ p 1 a 1 ∗ p 2 a 2 ∗ ⋯ ∗ p n a n a=p_0^{a0}*p_1^{a1}*p_2^{a2}*\cdots*p_n^{an} a=p0a0p1a1p2a2pnan
b = p 0 b 0 ∗ p 1 b 1 ∗ p 2 b 2 ∗ ⋯ ∗ p n b n b=p_0^{b0}*p_1^{b1}*p_2^{b2}*\cdots*p_n^{bn} b=p0b0p1b1p2b2pnbn
c = p 0 m i n ( a 0 , b 0 ) ∗ p 1 m i n ( a 1 , b 1 ) ∗ ⋯ ∗ p n m i n ( a n , b n ) c=p_0^{min(a0,b0)}*p_1^{min(a1,b1)}*\cdots*p_n^{min(an,bn)} c=p0min(a0,b0)p1min(a1,b1)pnmin(an,bn)
而对于lcm(a,b)而言
a = p 0 a 0 ∗ p 1 a 1 ∗ p 2 a 2 ∗ ⋯ ∗ p n a n a=p_0^{a0}*p_1^{a1}*p_2^{a2}*\cdots*p_n^{an} a=p0a0p1a1p2a2pnan
b = p 0 b 0 ∗ p 1 b 1 ∗ p 2 b 2 ∗ ⋯ ∗ p n b n b=p_0^{b0}*p_1^{b1}*p_2^{b2}*\cdots*p_n^{bn} b=p0b0p1b1p2b2pnbn
c = p 0 m a x ( a 0 , b 0 ) ∗ p 1 m a x ( a 1 , b 1 ) ∗ ⋯ ∗ p n m a x ( a n , b n ) c=p_0^{max(a0,b0)}*p_1^{max(a1,b1)}*\cdots*p_n^{max(an,bn)} c=p0max(a0,b0)p1max(a1,b1)pnmax(an,bn)
也就是说前面几步仍然按照上面做,先预处理处 1 ∼ 2 ∗ 1 0 9 1\sim\sqrt{2*10^9} 12109 的所有质数,然后对 a , b , c , d a,b,c,d a,b,c,d几个数分别对每个质因子进行求指数 m a , m b , m c , m d m_a,m_b,m_c,m_d ma,mb,mc,md
根据上面的 g c d gcd gcd本质:

  1. m a &gt; m c m_a&gt;m_c ma>mc,则 m x m_x mx只能等于 m c m_c mc
  2. m a = m c m_a=m_c ma=mc,则 m x m_x mx只需满足 m x ≥ m c m_x \geq m_c mxmc即可
  3. m a &lt; m c m_a&lt;m_c ma<mc,无解

同理,根据 l c m lcm lcm本质:

  1. m b &lt; m d m_b&lt;m_d mb<md,则 m x m_x mx只能等于 m d m_d md
  2. m a = m c m_a=m_c ma=mc,则 m x m_x mx只需满足 m x ≤ m d m_x \le m_d mxmd即可
  3. m a &gt; m c m_a&gt;m_c ma>mc,无解

总结一下,也就是说对于此时要分解的这个质因子 p p p而言吧,如果 m a &lt; m c ∣ ∣ m a &gt; m c m_a&lt;m_c||m_a&gt;m_c ma<mcma>mc无解,要么就是 m a &gt; m c &amp; &amp; m b &lt; m d &amp; &amp; m c = = m d m_a&gt;m_c\&amp;\&amp;m_b&lt;m_d\&amp; \&amp;m_c==m_d ma>mc&&mb<md&&mc==md,那么答案不变,不满足无解,另外一种就是 m a = = m c &amp; &amp; m b = = m d &amp; &amp; m c ≤ m d m_a==m_c\&amp;\&amp;m_b==m_d\&amp;\&amp;m_c\le m_d ma==mc&&mb==md&&mcmd,那么 m x m_x mx可以取 m c ∼ m d m_c\sim m_d mcmd之间的任何值,也就是将答案乘上 ( m d − m c + 1 ) (m_d-m_c+1) (mdmc+1),至于其余各种情况都是只能取一个值而已,也就不用多考虑了。
最后如果说是 a , b , c , d a,b,c,d a,b,c,d中有不是 1 1 1的,说明某个值有超过 ( d ) \sqrt(d) ( d)的质因子,此时和上面的分析也是差不多的,同时还是得先判断是否有 a &lt; c ∣ ∣ b &gt; a a&lt;c||b&gt;a a<cb>a的无解情况,如果说 c ! = 1 c!=1 c!=1说明如果 a ! = c ∣ ∣ c ! = d a!=c||c!=d a!=cc!=d都会导致无解,有解的情况在于 b 0 = = b 1 &amp; &amp; b 1 ! = 1 b0==b1\&amp;\&amp;b1!=1 b0==b1&&b1!=1时,还会再多乘上 2 2 2
c o d e : code: code:

#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
inline int read()
{
    
    
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int N=5e4+5;
int primes[N],cnt;
bool st[N];
void get_primes(int n)
{
    
    
    for(int i=2;i<=n;i++){
    
    
        if(!st[i])primes[cnt++]=i;
        for(int j=0;j<cnt&&primes[j]*i<=n;j++){
    
    
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}
int n,a0,a1,b0,b1;
pii factor[N],cntf;
int main()
{
    
    
    get_primes(50000);
    n=read();
    while(n--){
    
    
        a0=read();a1=read();b0=read();b1=read();
        int ans=1;
        bool flag=true;
        for(int i=0;i<cnt;i++){
    
    
            if(a0==1&&a1==1&&b0==1&&b1==1)break;
            int p=primes[i];
            int ma=0,mc=0,mb=0,md=0;
            while(a0%p==0){
    
    
                a0/=p;
                ma++;
            }
            while(a1%p==0){
    
    
                a1/=p;
                mc++;
            }
            while(b0%p==0){
    
    
                b0/=p;
                mb++;
            }
            while(b1%p==0){
    
    
                b1/=p;
                md++;
            }
            if(ma<mc||mb>md){
    
    
                flag=false;
                break;
            }
            if(ma>mc&&mb<md){
    
    
                if(mc!=md){
    
    
                    flag=false;
                    break;
                }
            }else if(ma==mc&&mb==md){
    
    
                if(mc<=md){
    
    
                    ans*=(md-mc+1);
                }else{
    
    
                    flag=0;
                    break;
                }
            }
        }
        if(!(a0==1&&a1==1&&b0==1&&b1==1)){
    
    
            if(a1>a0||b0>b1)flag=0;
            if(a1!=1&&a1!=b1&&a1!=a0)flag=0;
            if(b1==b0&&b1!=1)ans<<=1;
        }
        if(!flag)ans=0;
        printf("%d\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/zhouzi2018/article/details/99445568