1. 程式人生 > >【NOIP2009提高組】HK的趣味題

【NOIP2009提高組】HK的趣味題

題目背景

NOIP2009提高組試題2。

題目描述

Hanks博士是 BT(Bio-Tech,生物技術)領域的知名專家,他的兒子名叫 Hankson。現在,剛剛放學回家的 Hankson 正在思考一個有趣的問題。 今天在課堂上,老師講解了如何求兩個正整數 c1 和 c2 的最大公約數和最小公倍數。現在 Hankson 認為自己已經熟練地掌握了這些知識,他開始思考一個“求公約數”和“求公倍數”之類問題的“逆問題”,這個問題是這樣的:已知正整數 a0,a1,b0,b1,設某未知正整數 x 滿足: 1.x 和 a0 的最大公約數是 a1; 2.x 和 b0 的最小公倍數是 b1。 Hankson 的“逆問題”就是求出滿足條件的正整數 x。但稍加思索之後,他發現這樣的 x 並不唯一,甚至可能不存在。因此他轉而開始考慮如何求解滿足條件的 x 的個數。請你幫助他程式設計求解這個問題。

輸入格式

輸入第一行為一個正整數 n,表示有 n 組輸入資料。 接下來的 n 行每行一組輸入資料,為四個正整數 a0,a1,b0,b1,每兩個整數之間用一個空格隔開。輸入資料保證 a0 能被 a1 整除, b1 能被 b0 整除。

輸出格式

輸出共 n 行。每組輸入資料的輸出結果佔一行,為一個整數。 對於每組資料:若不存在這樣的 x ,請輸出 0 ;若存在這樣的 x ,請輸出滿足條件的 x 的個數。 樣例資料 1 輸入

2 41 1 96 288 95 1 37 1776

輸出

6 2

備註

【樣例說明】 第一組輸入資料,x 可以是 9、18、36、72、144、288,共有 6 個。 第二組輸入資料,x 可以是 48、1776,共有 2 個。 【資料範圍】 對於 50% 的資料,保證有 1≤a0,a1,b0,b1≤10000 且 n≤100。 對於 100% 的資料,保證有 1≤a0,a1,b0,b1≤2,000,000,000 且 n≤2000。

解析:

  數學推理題。   GCDGCD:   若gcd(a0,x)=a1gcd(a_0,x)=a_1   則有gcd(a0/a1,x/a1)=1gcd(a_0/a_1,x/a_1)=1   很好理解,因為如果不等與11,說明還有公約數,與a1a_1是最大公約數相悖。   LCMLCM:   若lcm(b0,x)=b1lcm(b_0,x)=b_1   則有lcm(b0,x)=b0xgcd(b0,x)=>gcd(b0,x)=b0xlcm(b0,x)=b0xb1lcm(b_0,x)=\frac{b_0*x}{gcd(b_0,x)}=>gcd(b_0,x)=\frac{b_0*x}{lcm(b_0,x)}=\frac{b_0*x}{b_1}

  由GCDGCD推出以來的結論可得:gcd(b1x,b1b0)gcd(\frac{b_1}{x},\frac{b_1}{b_0})   我們將兩式放在一起: gcd(a0/a1,x/a1)=1gcd(a_0/a_1,x/a_1)=1 gcd(b1x,b1b0)gcd(\frac{b_1}{x},\frac{b_1}{b_0})   容易發現合法的xx滿足既是a1a_1的倍數又是b1b_1的因數,並且滿足以上二式。   解法就很明顯了,在O(b1)O(\sqrt{b_1})的複雜度內列舉因數,驗證合法性即可。

程式碼:

#include <bits/stdc++.h>
using namespace std;

int n,a0,a1,b0,b1,p,q,ans;

int main()
{
	scanf("%d",&n);
	while(n--)
	{
	  scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
	  p=a0/a1,q=b1/b0,ans=0;
	  for(int i=1;i*i<=b1;i++)
	  {
	  	if(b1%i) continue;
	  	if(!(i%a1)&&__gcd(i/a1,p)==1&&__gcd(b1/i,q)==1) ans++;
	  	int j=b1/i;
	  	if(i==j) continue;
	  	if(!(j%a1)&&__gcd(j/a1,p)==1&&__gcd(b1/j,q)==1) ans++;
	  }
	  printf("%d\n",ans);
	}
	return 0;
}