【NOIP2009】洛谷1072 Hankson的趣味题

76 篇文章 0 订阅
36 篇文章 0 订阅

题目描述

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 的个数。请你帮

助他编程求解这个问题。 输入输出格式 输入格式:

第一行为一个正整数 n,表示有 n 组输入数据。接下来的 n 行每

行一组输入数据,为四个正整数 a0,a1,b0,b1,每两个整数之间用一个空格隔开。输入

数据保证 a0 能被 a1 整除,b1 能被 b0 整除。

输出格式:

输出文件 son.out 共 n 行。每组输入数据的输出结果占一行,为一个整数。

对于每组数据:若不存在这样的 x,请输出 0;

若存在这样的 x,请输出满足条件的 x 的个数;

一种显而易见的做法是将四个数质因数分解。然后对于某一个质因数p,设四个数的指数分别为ta0,ta1,tb0,tb1。则所求数的指数x满足:
x=ta1(ta0< ta1)
x>=ta1(ta0=ta1)
x∈∅(ta0< ta1)
x=tb1(tb0< tb1)
x<=tb1(tb1=tb0)
x∈∅(tb0> tb1)
由这些条件不难出解。
在进行质因数分解的时候,如果直接循环找因数可能超时。考虑到一个数大于1e5的因数最多只有一个,可以先筛出1..1e5之内的素数逐一计算,然后对大于1e5的因数再进行单独判断。

#include<cstdio>
#include<cstring>
int ta0[10000],ta1[10000],tb0[10000],tb1[10000],prm[10000];
bool have[100000];
int main()
{
    //freopen("son.in","r",stdin);
    //freopen("son.out","w",stdout);
    int i,j,k,m,n,x,y,z,a0,a1,b0,b1,cnt,T,tot=0,totnew;
    long long ans;
    bool flag;
    for (i=2;i<=100000;i++)
    {
        if (!have[i]) prm[++tot]=i;
        for (j=1;(long long)i*prm[j]<=100000;j++)
        {
            have[i*prm[j]]=1;
            if (i%prm[j]==0) break;
        }
    }
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        memset(ta0,0,sizeof(ta0));
        memset(ta1,0,sizeof(ta1));
        memset(tb0,0,sizeof(tb0));
        memset(tb1,0,sizeof(tb1));
        n=0;
        flag=1;
        for (i=1;i<=tot&&(a0>1||a1>1||b0>1||b1>1);i++)
          if (a0%prm[i]==0||a1%prm[i]==0||b0%prm[i]==0||b1%prm[i]==0)
          {
            n++;
            while (a0%prm[i]==0)
            {
                a0/=prm[i];
                ta0[n]++;
            }
            while (a1%prm[i]==0)
            {
                a1/=prm[i];
                ta1[n]++;
            }
            while (b0%prm[i]==0)
            {
                b0/=prm[i];
                tb0[n]++;
            }
            while (b1%prm[i]==0)
            {
                b1/=prm[i];
                tb1[n]++;
            }
        }
        if (a0>1)
        {
            n++;
            ta0[n]=1;
            if (a1==a0)
            {
                ta1[n]=1;
                a1=1;
            }
            if (b0==a0)
            {
                tb0[n]=1;
                b0=1;
            }
            if (b1==a0)
            {
                tb1[n]=1;
                b1=1;
            }
        }
        if (a1>1)
        {
            n++;
            ta1[n]=1;
            if (b0==a1)
            {
                tb0[n]=1;
                b0=1;
            }
            if (b1==a1)
            {
                tb1[n]=1;
                b1=1;
            }
        }
        if (b0>1)
        {
            n++;
            tb0[n]=1;
            if (b1==b0)
            {
                tb1[n]=1;
                b1=1;
            }
        }
        if (b1>1)
        {
            n++;
            tb1[n]=1;
        }
        flag=0;
        for (i=1;i<=n;i++)
          if (ta0[i]<ta1[i]||tb0[i]>tb1[i])
          {
            printf("0\n");
            flag=1;
            break;
          }
        if (flag) continue;
        ans=1;
        for (i=1;i<=n;i++)
          if (ta0[i]>ta1[i])
          {
            if (tb0[i]<tb1[i])
            {
                if (ta1[i]==tb1[i]) continue;
                else
                {
                    ans=0;
                    break;
                }
            }
            else
            {
                if (ta1[i]<=tb1[i]) continue;
                else
                {
                    ans=0;
                    break;
                }
            }
          }
          else
          {
            if (tb0[i]<tb1[i])
            {
                if (tb1[i]>=ta1[i]) continue;
                else
                {
                    ans=0;
                    break;
                }
            }
            else ans*=(tb1[i]-ta1[i]+1);
          }
        printf("%lld\n",ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值