【NOIP 2009】Hankson 的趣味题(唯一分解定理)
大致思路:
找办法确定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;
}
QQ40523591~欢迎一起学习交流~