1. 程式人生 > 其它 >數論 最大公約數 lgP7847題解

數論 最大公約數 lgP7847題解

題意:給定 \(n\),求方程 \(\frac 1 a - \frac 1 b=\frac 1 n\) 的所有解,且解必須滿足 \(\gcd(a,b,n)=1\)

以下內容搬運自官方題解:

轉化一下:

\[bn=a(b+n) \]\[a=\frac {bn} {b+n} \]

根據 \(\gcd(a,b,n)=1\),有:

\[\gcd(\frac {bn} {b+n},b,n)=\gcd(\frac {bn} {b+n},\gcd(b,n))=1 \]

接下來設 \(b=x \times \gcd(b,n),n=y \times \gcd(b,n)\),那麼一定有 \(\gcd(x,y)=1\)

於是:

\[\gcd(\frac {xy} {x+y} \times \gcd(b,n),\gcd(b,n))=1 \]\[\gcd(b,n)|(x+y) \]

\(\gcd(xy,x+y)=1,\frac {xy} {x+y} \times \gcd(b,n)\) 是整數,所以有 \((x+y)|\gcd(b,n)\)

於是 \(x+y=\gcd(b,n),b+n=\gcd(b,n) \times (x+y) = \gcd(b,n)^2\)

相當於求方程 \(x+n=\gcd(x,n)\)。(這裡的 \(x\) 在上面是 \(b\)

下面為了方便,設 \(\gcd(x,n)=d,c=dk\)

\[d^2=dk+x \]\[x=d \times (d-k) \]

然後對於 \(d\),列舉可行的 \(k\),最後檢查一下是否合法就行。

以上內容搬運自官方題解。

檢查是否合法的瓶頸在於,計算 \(\gcd(x,c)=\gcd(d(d-k),c)=\gcd(d(d-k),dk)=d \times \gcd(d-k,k)\)

這裡的 \(d-k\)\(k\) 一定有一個數不大於 \(\sqrt n\),所以根據 \(\gcd(a,b)=\gcd(a \bmod b,b)\),可以直接預處理一個 \(\sqrt n \times \sqrt n\) 的表。

再往下推,發現實際上是判斷 \(d\times \gcd(d-k,k)=d\)

,也就是判斷 \(d\)\(k\) 是否互質,所以打表可以使用一個 bool 型別的陣列來降低常數。

到這裡,複雜度已經變成 \(O(n\log n+T)\) 的了。在加強版中,這個做法只跑了 1s,並且卡掉了 \(O(n+T\sqrt n)\) 的暴力,還在原版跑到了300ms

#include<cstdio>
#include<vector>
typedef unsigned uint;
typedef unsigned long long ull;
const uint M=2e6;
uint T,mx,n[100005],ans1[M+5];bool _check[1420][1420];
ull ans[M+5],ans2[M+5];
inline ull min(const ull&a,const ull&b){
	return a>b?b:a;
}
signed main(){
	register uint i,j,x;
	scanf("%u",&T);_check[0][1]=true;
	for(i=1;i<=1415;++i)_check[1][i]=true;
	for(i=2;i<=1415;++i){
		for(x=0,j=i;j<=1415;++j){
			_check[i][j]=_check[x][i];
			if(++x==i)x=0;
		}
	}
	for(i=1;i<=T;++i)scanf("%u",n+i),mx=n[i]>mx?n[i]:mx;
	for(i=1;i<=mx;++i)ans2[i]=0x7f7f7f7f7f7f7f7f;
	for(i=1;i<=mx;++i){
		for(j=1,x=i;j<=i&&x<=mx;++j,x+=i){
			if(_check[i%j][j])ans2[x]=min(ans2[x],1ull*i*(i-j)),++ans1[x];
		}
	}
	for(ans1[i=1]=0;i<=mx;++i)ans1[i]+=ans1[i-1];
	for(i=1;i<=T;++i)printf(n[i]==1?"0\n":"%u %llu\n",ans1[n[i]],ans2[n[i]]);
}