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;
}
posted @ 2025-10-16 18:41  qwqSW  阅读(2)  评论(0)    收藏  举报