题目大意:求解c/d-e/d=a/b的任意正整数解,其中a,b已知,求c,d,e,f
官方题解:分成三种情况(判断带顺序)
<1>a,b不互质:因为有个公因数g=gcd(a,b),所以d和f就可以取b/g,凑吧凑吧就能出来解
<2>对b质因数分解,分解出来的质数个数小于等于1:注意,此时是建立在a,b互质的情况下,所以a/b的最简形式就是其本身,但是d,f<b,所以此时d和f的对应质因数的指数就不可能大于b的,c/d-e/f计算化简后的分母的对应质因数的指数也就不可能大于b的,所以这种情况无解
<3>b的相异质因数个数超过1个(第二种情况的补集):此时我们就可以得到互质的d和f,使得df=b,从而可以得到方程cf-ed=a,因为此时gcd(d,f)=1,所以肯定有解,用扩欧求解一下,得到c,之后找到特解:c=ac,然后我们求c的最小正数解:(c%d+d)%d,根据c的值再求出e的正数解。
这道题就细节很多,然后还容易T…质因数分解那块要打素数表,然后在分解前后还需要稍微剪剪枝。大概就是这样。
AC代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=2e6+10;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
if(b==0){
x=1,y=0;
return a;
}
LL d=exgcd(b,a%b,x,y);
LL z=x;x=y;y=z-y*(a/b);
return d;
}
vector<int> prime;//存储2~N的所有素数
bool v[maxn];//合数标记
void find_primes(int n)
{
memset(v,false,sizeof(v));
for(int i=2;i<=n;i++){
if(v[i]) continue;
prime.push_back(i);
//优化:从x^2开始,(x+1)*x...到N/x*x结束,标记为合数即可
for(int j=i;j<=n/i;j++) v[i*j]=true;
}
}
LL a,b,c,d,e,f;
int main()
{
find_primes(maxn-5);
int t;scanf("%d",&t);
while(t--){
scanf("%lld%lld",&a,&b);
if(b==1){
printf("-1 -1 -1 -1\n");
continue;
}
LL x,y;
LL g=exgcd(a,b,x,y);
if(g>1){
c=(a+g)/g,d=b/g;
e=1,f=b/g;
printf("%lld %lld %lld %lld\n",c,d,e,f);
continue;
}
if(!v[b]){
printf("-1 -1 -1 -1\n");
continue;
}
LL tempb=b;
d=1;
for(int i=0;i<prime.size();i++){
if(tempb%prime[i]==0){
while(tempb%prime[i]==0)
d*=prime[i],tempb/=prime[i];
break;
}
}
if(tempb==1){
printf("-1 -1 -1 -1\n");
continue;
}
f=b/d;
g=exgcd(f,d,c,e);
if(a%g!=0){
printf("-1 -1 -1 -1\n");
continue;
}
c=c*a/g;e=e*a/g;
LL d1=d/g;
c=(c%d1+d1)%d1;
LL k=(a-f*c)/d1;
if(k<0) k=0;
c=c+(k+1)*d1;
e=(f*c-a)/d;
printf("%lld %lld %lld %lld\n",c,d,e,f);
}
}