preparing

exgcd、裴蜀定理与有理数取余

exgcd

之前写得不好所以重写一遍

exgcd 即扩展欧几里得算法,常用来求 \(ax + by = \gcd{(a,b)}\) 的一组解。

设一组解为 \(x_1,y_1\),即 \(ax_1 + by_1 = \gcd{(a,b)}\),我们取 \(x_2,y_2\) 满足 \(bx_2 + (a\bmod b)y_2 = \gcd{(b,a\bmod b)}\)

显然 \(\gcd{(a,b)} = \gcd{(b,a\bmod b)}\),所以 \(ax_1 + by_1 = bx_2 + (a\bmod b)y_2\)

\(a\bmod b = a - b\left\lfloor\dfrac{a}{b}\right\rfloor\),展开得 \(ax_1 + by_1 = bx_2 + (a - b\left\lfloor\dfrac{a}{b}\right\rfloor)y_2\)

化简得 \(ax_1 + by_1 = ax_2 + b(x_2 - \left\lfloor\dfrac{a}{b}\right\rfloor y_2)\),故 \(x_1 = y_2 , y_1 = x_2 - \left\lfloor\dfrac{a}{b}\right\rfloor y_2\)

故我们求解的 \(x_1,y_1\) 可由 \(x_2,y_2\) 推出,考虑其满足的条件与原方程相同,所以可以迭代求解 \(x_2 = x_3,y_2 = y_3\)……

直至 \(b' = 0\) 为止,此时取 \(x_n = 1,y_n = 0\) 显然满足 \(a'x_n + 0\cdot y_n = \gcd(a',0) = a'\)

裴蜀定理

内容

裴蜀定理指出:\(ax + by = z\) 有解 \(\Leftrightarrow \gcd(a,b) | z\)

  • 充分性
    设方程解为 \(x,y\)
    显然 \(\gcd(a,b) | a,\gcd(a,b) | b\),故 \(\gcd(a,b) | (ax + by)\)\(\gcd(a,b) | z\)

  • 必要性
    类似 \(\operatorname{exgcd}\) 的过程,设 \(z = k\gcd(a,b)\),则不断迭代到最后一定有 \(b' = 0\),此时取 \(x_n = k,y_n = 0\) 显然满足 \(a'x_n + 0\cdot y_n = k\gcd(a',0) = ka'\)

推广及推论

\(\sum\limits_{i=1}^{n}a_ix_i = z\) 有解 \(\Leftrightarrow \gcd(a_1,a_2,\cdots,a_n) | z\)

\(a,b\) 互质的充要条件是 \(a_x + b_y = 1\) 有解。

例:P5656 【模板】二元一次不定方程 (exgcd)

题面:给定方程 \(ax + by = c\),首先判断其有无整数解。有整数解的情况下:若有正整数解则再输出正整数解的组数和 \(x,y\) 分别在所有正整数解中的最值,若无正整数解则再输出 \(x,y\) 分别在所有整数解中出现的正整数最小值。

显然我们由裴蜀定理可以先解决第一问:原方程有整数解当且仅当 \(\gcd(a,b) | c\)

若有整数解,我们用 \(\operatorname{exgcd}\) 可以先解一组 \(x',y'\) 满足 \(ax' + by' = \gcd(a,b)\),所以得到原方程的一组解 \(\begin{cases}x_0 = \dfrac{cx'}{\gcd(a,b)}\\y_0 = \dfrac{cy'}{\gcd(a,b)}\end{cases}\)

接下来我们要构造出通解,形如 \(\begin{cases}x = x_0 + kd_x\\y = y_0 - kd_y\end{cases}(k\in \Z)\)

注意到,原方程等价于 \(y = \dfrac{c - ax}{b}\)\(x = \dfrac{c - by}{a}\),所以增量 \(d_x,d_y\) 应该满足 \(b | ad_x , a | bd_y\)。又要满足增量最小,所以取 \(\begin{cases}d_x = \dfrac{b}{\gcd(a,b)}\\d_y = \dfrac{a}{\gcd(a,b)}\end{cases}\),即通解为 \(\begin{cases}x = x_0 + k\cdot \dfrac{b}{\gcd(a,b)}\\y = y_0 - k\cdot\dfrac{a}{\gcd(a,b)}\end{cases}(k\in \Z)\)

接下来判断有无正整数解。限制 \(x > 0\),即 \(x_0 + kd_x > 0\),得到 \(k > -\dfrac{x_0}{d_x}\),同理得到 \(y_0 - kd_y > 0\)\(k < \dfrac{y_0}{d_y}\)

于是有 \(-\dfrac{x_0}{d_x} < k < \dfrac{y_0}{d_y}\),又 \(k \in \Z\),有 \(\left\lceil\dfrac{-x_0 + 1}{d_x}\right\rceil\le k \le\left\lfloor\dfrac{y_0 - 1}{d_y}\right\rfloor\)

接下来显然若 \(\left\lfloor\dfrac{y_0 - 1}{d_y}\right\rfloor < \left\lceil\dfrac{-x_0 + 1}{d_x}\right\rceil\) 则无正整数解。那么,\(x\) 可取到的最小正整数值就是取 \(k = \left\lceil\dfrac{-x_0 + 1}{d_x}\right\rceil\)\(x\) 的值,\(y\) 可取到的最小正整数值就是取 \(k = \left\lfloor\dfrac{y_0 - 1}{d_y}\right\rfloor\)\(y\) 的值。

否则若 \(\left\lfloor\dfrac{y_0 - 1}{d_y}\right\rfloor \ge \left\lceil\dfrac{-x_0 + 1}{d_x}\right\rceil\) 就有正整数解,\(k = \left\lceil\dfrac{-x_0 + 1}{d_x}\right\rceil\)\(x\) 最小 \(y\) 最大,\(k = \left\lfloor\dfrac{y_0 - 1}{d_y}\right\rfloor\)\(x\) 最大 \(y\) 最小。

#include<iostream>
#include<cstdio>
#include<cmath>
#define ll long long
using namespace std;
int T,a,b,c,g,dx,dy; ll x,y,l,r; int ggcd(int aa,int bb){if(!bb) return aa; return ggcd(bb,aa%bb);}
void exgcd(int aa,int bb){if(bb==0){x=1LL; y=0LL; return;} exgcd(bb,aa%bb); ll tmp=x; x=y; y=tmp-(aa/bb)*y;}
int main(){
    scanf("%d",&T); while(T--){
        scanf("%d%d%d",&a,&b,&c); g=ggcd(a,b); if(c%g!=0){printf("-1\n"); continue;}
        dx=b/g; dy=a/g; exgcd(a,b); x*=(c/g); y*=(c/g); l=ceil(1.0*(1-x)/dx); r=floor(1.0*(y-1)/dy);
        if(r<l){printf("%lld %lld\n",x+l*dx,y-r*dy); continue;}
        printf("%lld %lld %lld %lld %lld\n",r-l+1,x+l*dx,y-r*dy,x+r*dx,y-l*dy);
    }
    return 0;
}

有理数取余

以洛谷的模板为例,要求 \(\dfrac{a}{b}\bmod 19260817\) 的值,注意 \(a\in [0,10^{10001}]\cap N,b\in [1,10^{10001}]\cap N^*\)

原式即求 \(x\) 使得 \(bx\equiv a\pmod{19260817}\)

显然有 \(b\dfrac{x}{a}\equiv 1\pmod{19260817}\)

而使用 \(\operatorname{exgcd}\),我们可以求解出这样的 \(x_0 = \dfrac{x}{a}\),所以就能得出 \(x\)

注意此处有解建立在 \(\gcd(b,19260817) = 1\) 的基础上,否则无解,又后者是一个质数,所以只要判断 \(b\) 是不是其倍数即可。

#include<iostream>
#include<cstdio>
#define ll long long
#define mod 19260817
using namespace std;
inline ll read(){
    ll res=0,neg=1; char ch=getchar(); while(!isdigit(ch)){if(ch=='-') neg=-1; ch=getchar();}
    while(isdigit(ch)){res=(res<<3)+(res<<1)+ch-'0'; res%=mod; ch=getchar();} return neg*res;
}
ll a,b,x,y,ans; void exgcd(ll a,ll b){if(b==0){x=1;y=0;return;} exgcd(b,a%b); ll tmp=x; x=y; y=tmp-(a/b)*y;}
int main(){
    a=read(); b=read(); if(b==0){printf("Angry"); return 0;} exgcd(b,mod); x=((x%mod+mod)%mod*a)%mod;
    printf("%lld",x);
    return 0;
}
posted @ 2022-11-10 19:39  qzhwlzy  阅读(48)  评论(0编辑  收藏  举报