【题解】 P1072 Hankson 的趣味题
\(Description:\)
给定\(a_0,a_1,b_0,b_1\),求所有满足\(gcd(x,a_0)=a_1\),\(lcm(x,b_0)=b_1\)的x的个数。
\(Sample\) \(Input\):
2
41 1 96 288
95 1 37 1776
\(Sample\) \(Output\):
6
2
数论题都那么毒瘤吗?
一开始除了暴力毫无思路。。。
暴力:从\(a_1->b_1\)枚举。。。。。。
没有思路,化个式子放松放松:
\(a_0=a_1*k_1\)
\(x=a_1*k_2\)
\(b_1=b_0*k_3\)
\(b_1=x*k_4\)
震惊!发现x是\(a_1\)的倍数,是\(b_1\)的因数。
于是想着随便枚举\(b_1\)的因数,gcd,lcm判断。
喵的过了。。。。。。
要不要看代码呀?
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,ans;
int a0,b0,a1,b1;
inline int gcd(int a,int b){
int tmp=0;
while(b){
if(a<b)swap(a,b);
tmp=a%b;
a=b;
b=tmp;
}
return a;
}
inline int lcm(int a,int b){
return (a*b)/gcd(a,b);
}
signed main(){
scanf("%lld",&T);
while(T--){
ans=0;
scanf("%lld%lld%lld%lld",&a0,&a1,&b0,&b1);
for(int i=1;i*i<=b1;++i){
if(b1%i==0){
if(i%a1==0 && gcd(i,a0)==a1 && lcm(i,b0)==b1)ans++;
if(i*i==b1)break;
int ret=b1/i;
if(ret%a1==0 && gcd(ret,a0)==a1 && lcm(ret,b0)==b1)ans++;
}
}
printf("%lld\n",ans);
}
return 0;
}
然而题解里还有一种更强的做法:
首先一个推论:
对于\(gcd(a,b)=c\),\(gcd(a/c,b/c)=1\)
证明:
设\(a=c*k_1\),\(b=c*k_2\)
那么如果推论不成立,那么一定有\(k_1*k_2\)里还有a,b公因数。
那么可以继续拆分
那么原式变为:
\(gcd(x,a_0)=a_1\) \(=>\) \(gcd(x/a_1,a_0/a_1)=1\)
\(lcm(x,b_0)=b1\) \(=>\) \(gcd(b_1/b_0,b_1/x)=1\)
这样枚举范围小一点,照理说会快,然而我却慢了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T,ans;
int a0,b0,a1,b1;
inline int gcd(int a,int b){
int tmp=0;
while(b){
if(a<b)swap(a,b);
tmp=a%b;
a=b;
b=tmp;
}
return a;
}
inline int lcm(int a,int b){
return (a*b)/gcd(a,b);
}
signed main(){
scanf("%lld",&T);
while(T--){
ans=0;
scanf("%lld%lld%lld%lld",&a0,&a1,&b0,&b1);
for(int i=1;i*i<=b1;++i){
if(b1%i==0){
if(i%a1==0 && gcd(b1/b0,b1/i)==1 && gcd(i/a1,a0/a1)==1)ans++;
if(i*i==b1)break;
int ret=b1/i;
if(ret%a1==0 && gcd(b1/b0,b1/ret)==1 && gcd(ret/a1,a0/a1)==1)ans++;
}
}
printf("%lld\n",ans);
}
return 0;
}