HDU5974 - The 2016 ACM-ICPC Asia Dalian Regional Contest - D - A Simple Math Problem - (数论LCM,dfs)

本文详细解析了HDU 5974题目的解决方案,通过分解质因数的方法寻找满足X+Y=a且LCM(X,Y)=b的X和Y值,提供了两种代码实现方式并讨论了优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接http://acm.hdu.edu.cn/showproblem.php?pid=5974

题意:给出a和b,让找到X和Y满足:

  1. X+Y=a;
  2. LCM(X,Y)=b;

LCM为最小公倍数,(1≤a≤2*10^4),(1≤b≤10^9),注意:测试样例很多。

解析

  • 首先考虑lcm的性质,假设有等式lcm(a,b)=x;
  • 将x分解质因数得:x=p1^num1*p2^num2...*pk^numk。
  • 将a分解质因数得:a=p1^numa1*p2^numa2...*pk^numak。
  • 将b分解质因数得:b=p1^numb1*p2^numb2...*pk^numbk。
  • 则num1=max(numa1,numb1),num2=max(numa2,numb2)...numk=max(numak,numbk)
  • 注意:a,b与x分解的质因子相同是因为,a和b的质因子也一定是x的质因子,而如果x的某个质因子pi如果不是a的质因子,只需使得a等式中的pi的幂为0即可。

 所以这里知道了b之后就有了一种枚举X与Y的方式:

将X看做k项相乘,即X=[p1^0或p1^2...或p1^num1]*[p2^0或p2^2...或p2^num1]...*[pk^0或pk^2...或pk^num1],然后怎么得到对应的Y使得LCM(X,Y)=b呢?

  • 这里枚举到X的第i项为pi^tx,假设Y的第i项为pi^ty.
  • 如果tx<numi那么由于max(tx,ty)=numi,那么ty=numi;
  • 如果tx==numi那么由于max(tx,ty)=numi,那么ty取值范围为0~numi。

综上dfs枚举X,Y判断是否等于a即可。但是上述方法复杂度是比较高的,在杭电上是920ms卡过的,所以不是正解,看别的题解上都是推了个公式解一下来直接求的X,Y,时间用的会少一些。

最后注意题目中的超级大天坑:最后输出的X,Y一定保证小的在前。这一点比赛时卡了我一个多小时,TMD。

代码(920ms)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

bool flag;
ll a,b,len,ansx,ansy;
ll p[105],num[105];

void init(ll n)//分解质因子
{
    len=0;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            p[++len]=i;
            num[len]=0;
        }
        while(n%i==0)
        {
            num[len]++;
            n=n/i;
        }
    }
    if(n>1)
    {
        p[++len]=n;num[len]=1;
    }
}

ll fpow(ll n,ll x)//求n^x
{
    ll ans=1;
    for(int i=1;i<=x;i++)
        ans=ans*n;
    return ans;
}

//step是当前处理到第step个因子,sum1是X前step-1项的累乘,sum2是Y前step-1项的累乘
void dfs(ll step,ll sum1,ll sum2)
{
    //x的第step项取p[step]^i,此时Y的第step 项一定是p[step]^num[step]
    for(int i=0;i<num[step];i++)
    {
        if(flag) return;
        if(step<len)
        {
            dfs(step+1,sum1*fpow(p[step],i),sum2*fpow(p[step],num[step]));
        }
        else
        {
            ll x=sum1*fpow(p[step],i);
            ll y=sum2*fpow(p[step],num[step]);
            //cout<<x<<"            "<<y<<endl;
            if(x+y==a)
            {
                flag=true;
                ansx=x;ansy=y;
                return;
            }
        }
    }

    //x的第step项固定取p[step]^num[step],然后枚举Y的第step项
    for(int i=0;i<=num[step];i++)
    {
        if(flag) return;
        if(step<len)
        {
            dfs(step+1,sum1*fpow(p[step],num[step]),sum2*fpow(p[step],i));
        }
        else
        {
            ll x=sum1*fpow(p[step],num[step]);
            ll y=sum2*fpow(p[step],i);
            //cout<<x<<" "<<y<<endl;
            if(x+y==a)
            {
                flag=true;
                ansx=x;ansy=y;
                return;
            }
        }
    }
}

int main()
{
    while(scanf("%lld%lld",&a,&b)!=EOF)
    {
        if(a==1)
        {
            printf("No Solution\n");
            continue;
        }
        if(b==1)
        {
            if(a==2)
                printf("1 1\n");
            else
                printf("No Solution\n");
            continue;
        }
        init(b);
        flag=false;
        dfs(1ll,1ll,1ll);
        if(flag)
        {
            if(ansx>ansy) swap(ansx,ansy);
            printf("%lld %lld\n",ansx,ansy);
        }
        else
            printf("No Solution\n");
    }
    return 0;
}

筛素数优化一下分解质因数,时间确实会少一些。

代码(795ms):

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

bool flag;
ll a,b,ansx,ansy;

const ll MAXN=1e6+5;
ll c,prime[MAXN];//c是素数个数78499
bool vis[MAXN+10];

void init_prime()//筛素数
{
    ll m=sqrt(MAXN+0.5);
    int i,j;
    c=0;
    for(i=2;i<=m;i++)
    {
        if(!vis[i])
        {
            for(j=i*i;j<MAXN;j+=i)
            {
                vis[j]=true;
            }
        }
    }
    for(i=2;i<MAXN;i++)
    {
        if(!vis[i])
            prime[c++]=i;
    }
}

ll len,p[105],num[105];
void init_n(ll n)//对n分解质因子
{
    len=0;
    for(int i=0; i<c&&n>1; i++)
    {
        if(n%prime[i]==0)
        {
            p[++len]=prime[i];
            num[len]=0;
        }
        while(n%prime[i]==0)
        {
            num[len]++;
            n=n/prime[i];
        }
    }
}

ll fpow(ll n,ll x)//求n^x
{
    ll ans=1;
    for(int i=1;i<=x;i++)
        ans=ans*n;
    return ans;
}

//step是当前处理到第step个因子,sum1是X前step-1项的累乘,sum2是Y前step-1项的累乘
void dfs(ll step,ll sum1,ll sum2)
{
    //x的第step项取p[step]^i,此时Y的第step 项一定是p[step]^num[step]
    for(int i=0;i<num[step];i++)
    {
        if(flag) return;
        if(step<len)
        {
            dfs(step+1,sum1*fpow(p[step],i),sum2*fpow(p[step],num[step]));
        }
        else
        {
            ll x=sum1*fpow(p[step],i);
            ll y=sum2*fpow(p[step],num[step]);
            //cout<<x<<"            "<<y<<endl;
            if(x+y==a)
            {
                flag=true;
                ansx=x;ansy=y;
                return;
            }
        }
    }

    //x的第step项固定取p[step]^num[step],然后枚举Y的第step项
    for(int i=0;i<=num[step];i++)
    {
        if(flag) return;
        if(step<len)
        {
            dfs(step+1,sum1*fpow(p[step],num[step]),sum2*fpow(p[step],i));
        }
        else
        {
            ll x=sum1*fpow(p[step],num[step]);
            ll y=sum2*fpow(p[step],i);
            //cout<<x<<" "<<y<<endl;
            if(x+y==a)
            {
                flag=true;
                ansx=x;ansy=y;
                return;
            }
        }
    }
}

int main()
{
    init_prime();
    while(scanf("%lld%lld",&a,&b)!=EOF)
    {
        if(a==1)
        {
            printf("No Solution\n");
            continue;
        }
        if(b==1)
        {
            if(a==2)
                printf("1 1\n");
            else
                printf("No Solution\n");
            continue;
        }
        init_n(b);
        flag=false;
        dfs(1ll,1ll,1ll);
        if(flag)
        {
            if(ansx>ansy) swap(ansx,ansy);
            printf("%lld %lld\n",ansx,ansy);
        }
        else
            printf("No Solution\n");
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值