扩展GCD即求解ax+by=c(a,b,c一般为已知整数)此类的方程所用方法。
其精华部分就在于GCD那个函数部分。证明等详见百度百科。
先求ax+by=GCD(a,b) 保证有解
那么对于原方程:
有解条件:c mod GCD(a,b)=0
一组解:x*(c/GCD(a,b)),y*(c/GCD(a,b))。
这样,我们就能在至多O(logn)的时间内求出一组解了。
有一个显然的性质是:x加上b且y减去a,方程仍满足。
那么我们要取一个对于x(或y)的最小非负整数解时,就可以这样做:
x=(x mod b+b) mod b (b>0)
(x mod b-b) mod b (b<0)
即(x mod b+abs(b)) mod b
b=0的话。。。这个就只有唯一解了。
那么对于求解线性模方程组,用中国剩余定理的话,有一定的局限性,即模数必须两两互质,而扩展GCD就没有这个限制了。
大致解法就是把方程合并,对于两个方程:
X mod a=b
X mod a'=b
设X=ak+b=a'k'+b'
则ak-a'k'=b'-b,a,-a',(b'-b)代入方程求出k,k'(同时判无解),然后算出X。
这样两个方程就合并成了X mod LCM(a,a')=X 很好理解,满足这一方程的X必定能同时满足原来的两个原方程,反过来也成立。这样就合并N-1次,求出最后的余数。
PKU1061
var x,y,n,m,L,gg,xx,yy,zz:int64; function gcd(a,b:int64;var x,y:int64):int64; var tmp:int64; begin if b=0 then begin gcd:=a;x:=1;y:=0;end else begin gcd:=gcd(b,a mod b,x,y); tmp:=x;x:=y;y:=tmp-a div b*y; end; end; begin readln(x,y,m,n,L); gg:=gcd(m-n,L,xx,yy); if ((y-x) mod L) mod gg<>0 then writeln('Impossible') else begin //writeln((((y-x) mod L) div gg*xx) mod L); zz:=(((y-x) mod L) div gg*xx) mod L; zz:=(zz+L) mod L; writeln(zz); end; end.
线性模方程:
var t,l,n,i:longint;gg,x,y:int64;pd:boolean; a,b:array[1..10] of int64; function gcd(a,b:int64;var x,y:int64):int64; var tmp:int64; begin if b=0 then begin gcd:=a;x:=1;y:=0;end else begin gcd:=gcd(b,a mod b,x,y); tmp:=x;x:=y;y:=tmp-a div b*y; end; end; begin readln(t); for l:=1 to t do begin readln(n); for i:=1 to n do read(a[i]);readln; for i:=1 to n do read(b[i]);readln; pd:=true; for i:=2 to n do begin gg:=gcd(a[i-1],-a[i],x,y); if (b[i]-b[i-1]) mod gg<>0 then begin pd:=false;break;end; b[i]:=x*((b[i]-b[i-1]) div gg)*a[i-1]+b[i-1]; a[i]:=a[i-1]*a[i] div abs(gg); b[i]:=(b[i] mod a[i]+a[i]) mod a[i]; end; write('Case ',l,': '); if pd then if b[n]=0 then writeln(a[n]) else writeln(b[n]) else writeln(-1); end; end.