1. 程式人生 > 實用技巧 >[洛谷P1072]Hankson 的趣味題「數論」

[洛谷P1072]Hankson 的趣味題「數論」

[洛谷P1072]Hankson 的趣味題「數論」

題目描述

Hanks 博士是 BT(Bio-Tech,生物技術) 領域的知名專家,他的兒子名叫 Hankson。現在,剛剛放學回家的 Hankson 正在思考一個有趣的問題。

今天在課堂上,老師講解了如何求兩個正整數 \(c_{1}\)\(c_{2}\) 的最大公約數和最小公倍數。現在 Hankson 認為自己已經熟練地掌握了這些知識,他開始思考一個“求公約數”和“求公倍數”之類問題的“逆問題”,這個問題是這樣的:已知正整數\(a_{0}\),\(a_{1}\),\(b_{0}\),\(b_{1}\) 設某未知正整數 \(x\) 滿足:

1. \(x\)

\(a_{0}\) 的最大公約數是 \(a_{1}\)​;

2. \(x\)\(b_{0}\)​ 的最小公倍數是 \(b_{1}\)

Hankson 的“逆問題”就是求出滿足條件的正整數 \(x\)。但稍加思索之後,他發現這樣的 \(x\) 並不唯一,甚至可能不存在。因此他轉而開始考慮如何求解滿足條件的 \(x\) 的個數。請你幫助他程式設計求解這個問題。

輸入格式

第一行為一個正整數 \(n\),表示有 \(n\) 組輸入資料。接下來的\(n\) 行每行一組輸入資料,為四個正整數 \(a_{0}\),\(a_{1}\),\(b_{0}\),\(b_{1}\),每兩個整數之間用一個空格隔開。輸入資料保證 \(a_{0}\)

​ 能被 \(a_{1}\) 整除,\(b_{1}\) 能被 \(b_{0}\) 整除。

輸出格式

\(n\) 行。每組輸入資料的輸出結果佔一行,為一個整數。

對於每組資料:若不存在這樣的 \(x\),請輸出 \(0\),若存在這樣的 \(x\),請輸出滿足條件的 \(x\) 的個數;

輸入輸出樣例

輸入 #1

2 
41 1 96 288 
95 1 37 1776 

輸出 #1

6 
2

思路分析

  • 複習數論時從虎歌部落格上看到這道題,拿來做一做
  • 求最大公約數和最小公倍數而已,上去從\(a_{1}\)暴力列舉,直接判斷,顯然超時,只拿了50分。
  • 這題是在素數的唯一分解定理下推薦的,這題竟然還和素數有關係???那就考慮用一下這個定理

素數唯一分解定理:

  • 定義:任何一個大於 1 的正整數都能被唯一分解為有限個素數的乘積
    那麼這題和這個定理到底有啥關係?
  • 本題應用:
    首先我們可以根據這個定理得出以下關係:
    \(a_{1} = p_{1}*p_{2}*...*p_{x}*p_{y}\)
    根據題意:\(gcd(x,a_{0})=a_{1}\)
    所以可以得出這樣的關係:
    \(x = p_{1}*p_{2}*p_{x}*p_{y}*...*p_{i}*p_{j} = a_{1}*...*p_{i}*p_{j}\)
    \(a_{0} = p_{1}*p_{2}*p_{x}*p_{y}*...*p_{m}*p_{n} = a_{1}*...*p_{m}*p_{n}\)
    不難發現,\(x\)\(a_{0}\)除與\(a_{1}\)相同的部分素數以外,其餘的各自的組成素數各不相同,即\(x/a_{1}\)\(a_{0}/a_{1}\)互質,得出\(gcd(x/a_{1},a_{0}/a_{1})==1\)
  • 推廣結論:
    對於兩個正整數\(a,b\),設\(gcd(a,b)=k\),則存在\(gcd(a/k,b/k)=1\)
  • 應用結論:
    根據\(lcm(x,b_{0})=b_{1}\)得出\(gcd(x,b_{0}) = x*b_{0}/b_{1}\)
    最後可推出以下兩個式子
    \(gcd(x/a_{1},a_{0}/a_{1})=1\)
    \(gcd(b_{1}/b_{0},b_{1}/x)=1\)
    接下來我們只要列舉\(b_{1}\)的因子,並且這個因子是\(a_{1}\)的倍數,同時滿足以上兩式即可

ps:挨個打數學符號是真滴麻煩

程式碼

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read(){
   int s=0,w=1;
   char ch=getchar();
   while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
   while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
   return s*w;
}

int gcd(int x,int y){
	return y==0? x : gcd(y,x%y);
}
int lcm(int x,int y){
	return x*y/gcd(x,y);
}
int main(){
	int n;n = read();
	for(int i = 1;i <= n;i++){
		int a,a1,b,b1;
		a = read(),a1 = read(),b = read(),b1 = read();
		int ans = 0;
		for(int x=1;x*x<=b1;x++){//列舉到sqrt(b1)即可
        	if(b1%x==0){
                if(x%a1==0&&gcd(x/a1,a/a1)==1&&gcd(b1/b,b1/x)==1) ans++; //滿足條件
                int y=b1/x;//同時得出另一個因子
                if(x==y) continue; 
                if(y%a1==0&&gcd(y/a1,a/a1)==1&&gcd(b1/b,b1/y)==1) ans++;
            }
        }
		printf("%d\n",ans);
	}
	return 0;
}