【CH3201】【Luogu P1072】Hankson的趣味题

先预处理出$sqrt{2 imes {10}^9}$内的质数。

对于每个质数p,设a0,a1,b0,b1,x含p因子的个数分别为ma0,ma1,mb0,mb1,mx。

$1. qquad ecause gcd(x,a0)=a1,$

   $qquad herefore$有以下情况:

   (1)当$ma0<ma1$时,不符合最大公约数,x无解;

   (2)当$ma0=ma1$时,$mx ge ma1$;

   (3)当$ma0>ma1$时,$mx=ma1$,若小于则不满足,若大于,最大公约数可以更大。

$2. qquadecause lcm(x,b0)=b1,$

   $qquad herefore$有以下情况:

   (1)当$mb0>mb1$时,不符合最小公倍数,x无解;

   (2)当$mb0=mb1$时,$mx le mb1$;

   (3)当$mb0<mb1$时,$mx=mb1$,否则最小公倍数可以更小。

综合以上情况,当存在1.(1)或2.(1)的任意一个情况时,x无解;

有解的情况:

(1)当$ma0=ma1$且$mb0=mb1$时,若$ma1le mb1$,则$ma1 le mx le mb1$,有$mb1-ma1$个解;

(2)当$ma0=ma1$且$mb0<mb1$时,若$ma1le mb1$,则$mx=ma1 le mb1$,有一解;

(3)当$ma0>ma1$且$mb0=mb1$时,若$ma1le mb1$,则$mx=ma1 le mb1$,有一解;

(4)当$ma0>ma1$且$mb0<mb1$时,若$ma1=mb1$,则$mx=ma1=mb1$,有一解。

设因子p找到了$cnt_p$个解,

根据乘法原理,$ans=sum_{p|d(p>1)} cnt_p$

注意点:a0和b1可能很大,需要把它们当作p再计算;有些地方不要一直调试浪费时间,可以重打一遍,注意要注释原来的代码而不是删除。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long ll;
int n,a0,a1,b0,b1;
int ma0,ma1,mb0,mb1;
int m,pri[25002],v[45002],ans;
void prime()
{
    for(int i=2;i<=45000;i++)
    {
        if (!v[i]) pri[++m]=i;
        for(int j=1;j<=m;j++)
        {
            if (i*pri[j]>45000) break;
            v[i*pri[j]]=1;
            if (i%pri[j]==0) break;
        }
    }
}
void doit(int p)
{
    ma0=ma1=mb0=mb1=0;
    while(a0%p==0) {ma0++;a0=a0/p;}
    while(a1%p==0) {ma1++;a1=a1/p;}
    while(b0%p==0) {mb0++;b0=b0/p;}
    while(b1%p==0) {mb1++;b1=b1/p;}
    if (ma0<ma1||mb0>mb1) {ans=0;return;}
    if (ma0==ma1&&mb0==mb1)
    {
        if (ma1<=mb1) ans*=(mb1-ma1)+1;
        else ans=0;
        return;
    }
    if ((ma0==ma1&&mb0<mb1)||(ma0>ma1&&mb0==mb1))
    {
        if (ma1<=mb1) ans*=1;
        else ans=0;
        return;
    }
    if (ma0>ma1&&mb0<mb1)
    {
        if (ma1==mb1) ans*=1;
        else ans=0;
        return;
    }
}
int main()
{
    prime();
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        ans=1;
        for(int j=1;j<=m;j++)
            doit(pri[j]);
        if (a0>1) doit(a0);
        if (b1!=a0&&b1>1) doit(b1);
        printf("%d
",ans);
    }
    return 0;
}