题解 luogu.【XR-3】小道消息 & luogu.[NOIP 2009 提高组] Hankson 的趣味题

题目

luogu.P1029 [NOIP 2001 普及组] 最大公约数和最小公倍数问题
luogu.[NOIP 2009 提高组] Hankson 的趣味题

两道考察同一个结论的题目。我们主要证明一下这个结论,建模是非常朴素的。所以重点还是数学的演绎推理。

题意建模

都说可以模拟。确实如此。但是这样只能拿到部分分。我们还是手推一下这些定理与性质。

算法分析

这两个题无非就考了一个枚举,一个 \(\gcd()\) 函数,所以实现并不难。结合代码,发现都有这么一句:

for(int i=1;i<=y/i;i++)
	if(y%i==0)
	{
		if(gcd(i,y/i*x)==x) cnt++;
		if(y/i!=i)
			if(gcd(y/i,i*x)==x)
				cnt++;
	}

以及:

for(int x=1;x<=b1/x;x++)
	if(b1%x==0)
	{
		if(gcd(x,a0)==a1 && lcm(x,b0)==b1) ans++;
		if(b1/x!=x)
			if(gcd(b1/x,a0)==a1 && lcm(b1/x,b0)==b1)
				ans++; 
	}

这个代码片段是题的核心,依赖的是以下这个定理:

\(\left\{ \begin{array}{rcl} a_{1}=\gcd(x,a_{0}),\\ b_{1}=lcm(x,b_{0})\\ \end{array}\right. \)

且有
\(\left\{ \begin{array}{rcl} a_{1}|a_{0},\\ b_{0}|b_{1},\\ x|b_{1}\\ \end{array}\right. \)

则:
\(\left\{ \begin{array}{rcl} a_{1}=\gcd(b_{1}/x,a_{0})\\ b_{1}=lcm(b_{1}/x,b_{0})\ \end{array}\right. \)

这是很显然的。只需要导出 \(b_{1}/x \geqslant x\) ,以及\(a_{1}|x\),即可知道 \(a1|b_{1}/x\),所以就得证了。

只有在结合代码的时候,上述结论成立,因为有一个很强的条件是 \(b_{1}/x \geqslant x\)
这是由代码中的枚举顺序决定了的。

参考代码

#include<iostream>
using namespace std;
int gcd(int x,int y) { return y?gcd(y,x%y):x; }
int cnt;
int main()
{
	int x,y,p,q; cin>>x>>y;
	for(int i=1;i<=y/i;i++)
		if(y%i==0)
		{
			if(gcd(i,y/i*x)==x) cnt++;
			if(y/i!=i)
				if(gcd(y/i,i*x)==x)
					cnt++;
		}
	cout<<cnt<<endl;
	return 0;
}
#include<iostream>
using namespace std;
int gcd(int x,int y){ return y?gcd(y,x%y):x; }
int lcm(int x,int y){ return y/gcd(x,y)*x; }
void solve()
{
	int a0,a1,b0,b1; cin>>a0>>a1>>b0>>b1;
	int ans=0;
	for(int x=1;x<=b1/x;x++)
		if(b1%x==0)
		{
			if(gcd(x,a0)==a1 && lcm(x,b0)==b1) ans++;
			if(b1/x!=x)
				if(gcd(b1/x,a0)==a1 && lcm(b1/x,b0)==b1)
					ans++; 
		}
	cout<<ans<<endl;
}
int main()
{
	int t; cin>>t;
	while(t--) solve();
	return 0;
}

细节实现

注意不要爆 int 就可以。还要特判,不是完全平方数。

总结归纳

就是一个小结论。

posted @ 2025-08-04 09:12  枯骨崖烟  阅读(3)  评论(0)    收藏  举报