【NOIP 2009】Hankson 的趣味题(唯一分解定理)

本文介绍了一种利用质因数分解和唯一分解定理解决特定数学问题的算法,通过比较a0,a1,b0,b1的质因子次数来确定变量x的可能取值范围。该算法首先筛选出小于50000的所有质数,然后通过迭代检查每个质因子在四个数中的次数,以此推断x的上下限。

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

传送门

大致思路:

找办法确定x的范围

我们联想到gcd,lcd对于唯一分解定理的运用

即a,b,gcd(a,b)分解质因数后,gcd(a,b)每个质因子的次数是min(在a中出现的次数,b中出现的次数)

lcm变成max即可

那么也就是说 我们可以给a1,a0,b1,b0分解质因数,从而通过 每个质因子次数作比较 来推断x的取值范围

假设px,p0,p1分别是某个质因子在x,a0,a1里的次数

则有
{
1.若p0<p1 那么不满足gcd的关系,需舍掉(因为是取min)
2.若p0=p1 那么px可以取无限大 只要大于p1
3.若p0>p1 那么px只能取p1
}

lcm同理。

由于数的范围很大,2,000,000,000,我们肯定不能筛2,000,000,000以内的质数,考虑到每个数最多只拥有一个大于根号的质因子,我们只需筛到sqrt(2,000,000,000)~=50000即可,最后一个特大数再特判。

具体看代码注释

#include<bits/stdc++.h>
using namespace std;
int prime[10005],cnt;
bool vis[50005];
void Euler_pick(int n)
{
    memset(vis,false,sizeof(false));
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]) prime[++cnt]=i;
        for(int j=1;i*prime[j]<=n;j++)
        {
            vis[i*prime[j]]=true;
            if(i%prime[j]==0)   break;
        }
    }
}
int main()
{
    Euler_pick(50000); 
    int T;
    cin>>T;
    while(T--)
    {
        int a0,a1,b0,b1,ans=1;
        bool flag=false;
        cin>>a0>>a1>>b0>>b1;
        if(a1>a0||b1<b0)
        {
            cout<<"0"<<'\n';
            continue;
        }
        for(int i=1;i<=cnt;i++)
        {
            if(a0==a1==b0==b1==1)   break;
            int ka0=0,ka1=0,kb0=0,kb1=0;
        
            while(a0%prime[i]==0)   {ka0++;a0/=prime[i];}
            while(a1%prime[i]==0)   {ka1++;a1/=prime[i];} 
            while(b0%prime[i]==0)   {kb0++;b0/=prime[i];} 
            while(b1%prime[i]==0)   {kb1++;b1/=prime[i];} 
        
            if(ka0<ka1||kb0>kb1)    {flag=true;break;}
        
            int l1=ka1,l2=0,r1=100000,r2=kb1;
            //l1,l2分别是两个式子的下界限制,r1,r2是上界限制 
            //由唯一分解定理可知
            /*
              对于gcd 
              {
                    若ka0=ka1 此时是没有上界的 r1无限大 l1就是ka1 
                    若ka0>ka1 此时kx只能取ka1 所以l1=r1=ka1 
              }
              对于lcm
              {
                    若kb0=kb1 此时是有上界的 r2为kb1 没有下界  
                    若kb0<kb1,此时kx只能取kb1 所以l2=r2=kb2  
                    从此可以看出r2=kb1 
              } 
              这里我们代码的写法偷一点懒 
              */
            if(ka0>ka1) r1=ka1;
            if(kb0<kb1) l2=kb1;
            int l=max(l1,l2);
            int r=min(r1,r2);//小小取大,大大取小 
            if(r<l) {flag=true;break;};
            ans=(long long)ans*(r-l+1);
        }
        //分解sqrt范围内数据完毕 此时可能会有一个大于50000的质因子存在  
        if(!(a0==1&&a1==1&&b0==1&&b1==1))
        {
            if(a0<a1||b0>b1)    flag=true; 
            if(a1==a0&&a1!=1)   ans*=2;//a1==a0保证了剩下的数就是某个大质因子 而不是从上面break下来的 
            if(b1==b0&&b1!=1)   ans*=2; 
        }
        if(flag==true)  ans=0;
        cout<<ans<<'\n';
    }
    return 0;
}

转载于:https://www.cnblogs.com/Patrickpwq/articles/9643906.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值