扩展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.