P5656 【模板】二元一次不定方程 (exgcd)
hytallenxu · · 题解
观前提示
本题解不由 exgcd 算法实现。
做法说明
首先注意到
1. 判断无解
若
为什么呢?
设式子为
即原式可表达为
因为要使得式子有整数解,
2. 辗转相除法
将式子简化为
使用辗转相除法求得不完全商数组,证明留给下面。
3. 通解
使用不完全商数组求得
4. 细节
设通解为
将解表达为
具体细节看代码。
Code
时间复杂度为
#include <bits/stdc++.h>
#define int long long
#define mp make_pair
#define pb push_back
using namespace std;
using i64 = long long;
int n,t,cnt=1;
int q[40],p[40],Q[40];
pair<int,int> ans;
void divi(int x, int y){
while(x>1 && y>1){
q[cnt++]=x/y;
x-=(x/y)*y;
swap(x,y);
}
}
pair<int, int> solve2(int a, int b, int c){
divi(a,b);
if(a==1){auto ans=mp(c,0);return ans;}
if(b==1){auto ans=mp(0,c);return ans;}
p[0]=1;p[1]=q[1];
for(int i=2;i<cnt;i++) p[i]=q[i]*p[i-1]+p[i-2];
Q[0]=0;Q[1]=1;
for(int i=2;i<cnt;i++) Q[i]=q[i]*Q[i-1]+Q[i-2];
int x=((cnt-2)%2?-1:1)*Q[cnt-1],y=((cnt-1)%2?-1:1)*p[cnt-1];
auto ans=mp(x*c,y*c);
return ans;
}
void solve(){
for(int i=0;i<cnt;i++) p[i]=q[i]=Q[i]=0;
cnt=1;int a,b,c;cin>>a>>b>>c;
int gcd=__gcd(abs(a),abs(b));
if(c%gcd!=0){cout<<"-1\n"; return;}
if(gcd!=1){
a/=gcd,b/=gcd,c/=gcd; //将原式化成最简
int absa=abs(a),absb=abs(b),absc=abs(c);
ans=solve2(absa,absb,absc);
}else{ans=solve2(a,b,c);}
int x0=ans.first,y0=ans.second;
if(a<0) x0=-x0;if(b<0) y0=-y0;
int minx=x0%b,miny=y0%a;minx=(minx<=0?minx+b:minx);miny=(miny<=0?miny+a:miny); int maxx=(c-b*miny)/a, maxy=(c-a*minx)/b; //将通解提升至最小x/y解,并通过最小解求出最大解。
if(maxx<=0 || maxy<=0){cout<<(c-b*maxy)/a<<" "<<(c-a*maxx)/b<<endl;return;} //若最大解<0,则无正整数x,y解。
cout<<(maxx-minx)/b+1<<" "<<minx<<" "<<miny<<" "<<maxx<<" "<<maxy<<endl; //(maxx-minx)/b+1即为正整数解的数量。
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(nullptr);cout.tie(nullptr);
cin>>t;
while(t--) solve();
// 解的数量 = min((c/a-最小x)/b+1, (c/b-最小y)/a+1)
return 0;
}
证明
选自《初等数论》第三版,高等教育出版社。