【模板】二元一次不定方程 exgcd
posted on 2022-09-17 15:59:26 | under 模板 | source
code
LL mod(LL x,LL m){return(x%m+m)%m;}
LL exgcd(LL a,LL b,LL c,LL& x,LL& y){
if(!b) return x=c/a,y=0,a;
LL res=exgcd(b,a%b,c,y,x);
return y-=a/b*x,res;
}
LL solve(LL a,LL b,LL c){
LL x,y,d=exgcd(a,b,c,x,y);
return c%d==0?mod(x,b/d):-1;
}
template <class T>
tuple<T, T, T> exgcd(T a, T b, T c) {
if (!b) return {c / a, 0, a};
auto [x, y, d] = exgcd(b, a % b, c);
return {y, x - a / b * y, d};
}
template <class T>
T solveEquation(T a, T b, T c) { // get the vaild solution x of ax + by = c
auto [x, y, d] = exgcd(a, b, c);
auto mod = [](T x, T y) { return (x % y + y) % y; };
return c % d == 0 ? mod(x, b / d) : -1;
}
template <class T>
T getInv(T a, T p) {
return solveEquation(a, p, 1);
}
调用 solve(a,b,c)
能求得 \(ax+by=c\) 中 \(x\) 的最小非负整数解,无解返回 \(-1\)。
\(n\) 在模 \(P\) 意义下的逆元是 solve(n,P,1)
。
但是,为什么这样写对呢?
exgcd
我们想求的是这样一个东西的一组解 \((x,y)\):\(ax+by=\gcd(a,b)\)。
回忆一下我们求 \(\gcd\) 的过程,我们用到了 \(\gcd(a,b)=\gcd(b,a\bmod b)\) 的性质。
将原方程的 \((a,b)\) 全部替换成 \((b,a\bmod b)\) 也应该成立:
取模的定义:\(a\bmod b=a-\left\lfloor\frac{a}{b}\right\rfloor\cdot b\)(下文 \(\left\lfloor\frac{a}{b}\right\rfloor\) 写作 \(a/b\)),代入:
我们貌似看到了一组新的解 \((x'=y,y'=x-(a/b)\cdot y)\)。这就是 exgcd。将这个过程递归下去即可。
发现还有递归边界,此时 \(b=0\),原方程变为 \(ax=a\)(\(a\) 是原来的 \(\gcd(a,b)\)),取 \((x=1,y=0)\)(\(y\) 可以是任何数,但一般取 \(0\)),即为递归边界。
LL exgcd(LL a,LL b,LL& x,LL& y){
if(!b) return x=1,y=0,a;
LL res=exgcd(b,a%b,y,x);
return y-=a/b*x,res;
}
exgcd :: Integral a => (a, a) -> (a, a)
exgcd (a, 0) = (1, 0)
exgcd (a, b) = let (x, y) = exgcd (b, a `mod` b) in (y, x - (a `div` b) * y)
继续
exgcd 可以求出形如 \(ax+by=\gcd(a,b)\) 的一组整数特解 \((x_0,y_0)\)。
由裴蜀定理得,如果 \(\gcd(a,b)\not\mid c\) 那么直接跑路。
否则,等式两边同时乘 \(\frac{c}{\gcd(a,b)}\),得到原方程 \(ax+by=c\) 的特解 \((x_1=\frac{c}{\gcd(a,b)}\cdot x_0,y_1=\frac{c}{\gcd(a,b)}\cdot y_0)\)。
考虑这么一个式子,它和原方程等价:
显然 \(d\) 可以取到 \(1/\gcd(a,b)\),所以得到任意一组解 \((x,y)\) 都满足:
所以 \(x\) 的最小的正整数解是 \(x_1\bmod \frac{b}{\gcd(a,b)}\)。我们止步于此。
code-analysis
LL mod(LL x,LL m){return(x%m+m)%m;}
LL exgcd(LL a,LL b,LL c,LL& x,LL& y){
if(!b) return x=c/a,y=0,a;
//x=c/a,提前算好 x_1,这里 a 是 gcd
LL res=exgcd(b,a%b,c,y,x);
return y-=a/b*x,res;
}
LL solve(LL a,LL b,LL c){
LL x,y,d=exgcd(a,b,c,x,y);
return c%d==0?mod(x,b/d):-1;
//先判无解,如果有解就模
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/template-exgcd.html