P1072 [NOIP 2009 提高组] Hankson 的趣味题
(下面称 \(V\) 为 \(a_0,a_1,b_0,b_1\) 的值域)
我们在小学二年级就学过,对于两个正整数 \(a,b\) ,可以分别将它们表示为 \(mx,my\) ,其中 \(m=gcd(a,b),gcd(x,y)=1\)。
我们借这条性质略微表示一下题目里的 \(x,a_0,a_1,b_0,b_1\):
\( \begin{cases} x=m_1x_1 \\ a_0=m_1y_1 \\ a_1=m_1 \\ x=m_2x_2 \\ b_0=m_2y_2 \\ b_1=m_2x_2y_2 \\ \end{cases} \)
然后我们发现有几个量是可以求的: \(m_1=a_1,x_2=b_1/b_0,y_1=a_0/a_1\)。
接下来,我们考虑枚举 \(m_2\) ,这样剩下的 \(x_1,y_2\) 都可以求出来了。
正常来说,如果不质因数分解的话,从 \(1\) 逐个枚举到 \(b_0\) 的时间复杂度是 \(O(V)\) 的,无法接受。
但是我们小学二年级还学过, \(\sqrt b_0\) ~ \(b_0\) 的因子个数与 \(1\) ~ \(\sqrt b_0\) 的因子个数大致相等,可是 \(\sqrt b_0\) ~ \(b_0\) 的整数个数与 \(1\) ~ \(\sqrt b_0\) 就不是一个量级的。
所以我们枚举 \(1\) ~ \(\sqrt b_0\) 里的数,假设当前枚举到了某个因子 \(y\) ,那么 \(\sqrt b_0\) ~ \(b_0\) 里会存在一个数 \(z\) ,使得 \(y \times z = b_0\)。我们在枚举到 \(m_2=y\) 的时候顺带着算算 \(m_2=z\) 时是否有解就行了。
时间复杂度 \(O(n \sqrt V \log V)\)。
代码:
P1072
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(c<48){
if(c=='-') f=-1;
c=getchar();
}
while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x*f;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else write(x/10),putchar(x%10+'0');
}
int T,A0,A1,B0,B1,M1,Y1,X2,ans;
inline int gcd(int a,int b){
return (b==0?a:gcd(b,a%b));
}
inline int solve(int M2){
//计算当前枚举的m2是否有解
int Y2=B0/M2;
if(gcd(Y2,X2)>1){
//x2,y2不互质时非法
return 0;
}
if(M2*X2%M1!=0){
//x1不为整数时非法
return 0;
}
int X1=M2*X2/M1;
if(gcd(X1,Y1)>1){
//x1,y1不互质时非法
return 0;
}
return 1;
}
signed main(){
T=read();
while(T--){
ans=0;
A0=read(),A1=read(),B0=read(),B1=read();
M1=A1;X2=B1/B0;Y1=A0/A1;
int i;
for(i=1;i*i<B0;i++){
if(B0%i==0){
int res1=solve(B0/i);
ans+=res1;
int res2=solve(i);
ans+=res2;
}
}
if(i*i==B0){
//b0是平方数的情况需要特判,因为放在循环里会算两次
int res=solve(i);
ans+=res;
}
printf("%lld\n",ans);
}
return 0;
}