整数与整除
扩展欧几里得算法
-
裴蜀定理:若 a , b ∈ Z a,b\in Z a,b∈Z,对于任意整数 x , y x,y x,y, a x + b y = k gcd ( a , b ) \color{red}ax+by=k\gcd(a,b) ax+by=kgcd(a,b)。特别的,存在 x , y x,y x,y,使得 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b)
用来判断式子是否有解: a x + b y = m ax+by=m ax+by=m 有解 ⟺ gcd ( a , b ) ∣ m \iff \gcd(a,b)|m ⟺gcd(a,b)∣m
-
证明:
设 d = gcd ( a , b ) d=\gcd(a,b) d=gcd(a,b),则 d ∣ ( a x + b y ) d|(ax+by) d∣(ax+by)
不妨设 s s s 为 a x + b y ax+by ax+by 的最小正值,则 ∵ d ∣ s ∴ d ≤ s \because d|s\therefore d\le s ∵d∣s∴d≤s
令 q = a / s , r = a % s q=a/s,r=a\%s q=a/s,r=a%s,即 a = q s + r a=qs+r a=qs+r.
∴ r = a − q ( a x + b y ) = a ( 1 − q x ) + b ( − q y ) \therefore r=a-q(ax+by)=a(1-qx)+b(-qy) ∴r=a−q(ax+by)=a(1−qx)+b(−qy),即 r r r 为 a , b a,b a,b 的线性组合。
∵ 0 ≤ r < s 且 s 为 最 小 正 值 ∴ r = 0 , a = q s \because 0\le r<s\;且\;s为最小正值 \therefore r=0,a=qs ∵0≤r<s且s为最小正值∴r=0,a=qs,即 s ∣ a s|a s∣a.
同理 s ∣ b s|b s∣b.
故 s s s 为 a , b a,b a,b 的公约数,于是 ∵ s ∣ d ∴ s ≤ d \because s|d\therefore s\le d ∵s∣d∴s≤d.
于是 s = d s=d s=d 得证。
-
推论: a , b a,b a,b 互质 ⟺ \iff ⟺ 存在整数 x , y x,y x,y,使得 a x + b y = 1 ax+by=1 ax+by=1
-
-
扩展欧几里得:
-
目的:计算 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的解 x , y x,y x,y
-
方法:根据普通辗转相除法,到达递归边界
b=0,a=gcd(a,b)
时,发现有解 x = 1 , y = 0 x=1,y=0 x=1,y=0,即 a ∗ 1 + b ∗ 0 = gcd ( a , b ) a*1+b*0=\gcd(a,b) a∗1+b∗0=gcd(a,b)。 -
注意:此时 a , b a,b a,b 已不是开始的 a , b a,b a,b,于是根据递归算法和下一状态 b , a % b b,a\%b b,a%b 的通解 x 1 , y 1 x_1,y_1 x1,y1(即 b ∗ x 1 + ( a % b ) ∗ y 1 = gcd b*x_1+(a\%b)*y_1=\gcd b∗x1+(a%b)∗y1=gcd)求出当前状态的通解。
∵ a % b = a − ( a / b ) ∗ b ∴ b ∗ x 1 + ( a % b ) ∗ y 1 = b ∗ x 1 + ( a − ( a / b ) ∗ b ) ∗ y 1 = a ∗ y 1 + b ∗ ( x 1 − a / b ∗ y 1 ) = gcd 即 { x = y 1 y = x 1 − a / b ∗ y 1 \because a\%b=a-(a/b)*b\\ \therefore b*x_1+(a\%b)*y_1=b*x_1+(a-(a/b)*b)*y_1=a*{\color{red}y_1}+b*{\color{red}(x_1-a/b*y_1)}=\gcd\\ 即\begin{cases}x=y_1\\y=x_1-a/b*y_1 \end{cases} ∵a%b=a−(a/b)∗b∴b∗x1+(a%b)∗y1=b∗x1+(a−(a/b)∗b)∗y1=a∗y1+b∗(x1−a/b∗y1)=gcd即{x=y1y=x1−a/b∗y1int exgcd(int a, int b, int &x, int &y){ if(!b){ x = 1, y = 0; return a; } int gcd = exgcd(b, a % b, x, y); int tmp = y; y = x - (a / b) * x; x = tmp; return gcd; }
-
例题:P2613 【模板】有理数取余
-
题目:计算有理数 a b ( m o d p ) \dfrac{a}{b} \pmod{p} ba(modp) 的最小正数解
-
思路:即 a ≡ b x ( m o d p ) a\equiv bx\pmod{p} a≡bx(modp),也就是 b x ≡ 1 ( m o d p ) bx\equiv1\pmod{p} bx≡1(modp) 的解的 a a a 倍。处理大数据时只需要用 a % p , b % p a\%p,b\%p a%p,b%p 代替 a , b a,b a,b 即可。
//计算 b * x (mod mod) == a 的最小正数解.即 b * x (mod mod) == 1 的解的 a 倍。 #include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define ll long long const int mod = 19260817; ll a = 0, b = 0, x = 0, y = 0; string A, B; ll exgcd(ll a, ll b, ll &x, ll &y){ if(b == 0){ x = 1, y = 0; return a; } ll gcd = exgcd(b, a % b, x, y); ll tmp = y; y = x - (a / b) * y; x = tmp; return gcd; } int main(){ cin >> A >> B; rep(i, 0, A.length() - 1){ (a *= 10) += (A[i] - '0'); a %= mod; } rep(i, 0, B.length() - 1){ (b *= 10) += (B[i] - '0'); b %= mod; } if(b == 0 && a != 0){ puts("Angry!"); return 0; } exgcd(b, mod, x, y); x *= a; x = (x % mod + mod) % mod; cout << x << endl; }
例题:P1516 青蛙的约会
-
题目:计算 x x + m x ( m o d L ) = y y + n x ( m o d L ) xx+mx\pmod{L}=yy+nx\pmod{L} xx+mx(modL)=yy+nx(modL) 的最小正数解 x x x
-
思路:
( n − m ) x ≡ x x − y y ( m o d L ) a x ≡ c ( m o d b ) a x + b y = c \begin{aligned} (n-m)x&\equiv xx-yy\pmod{L}\\ ax&\equiv c\pmod{b}\\ ax+by&=c \end{aligned} (n−m)xaxax+by≡xx−yy(modL)≡c(modb)=c-
有解 ⟺ gcd ( a , b ) ∣ c \iff\gcd(a,b)|c ⟺gcd(a,b)∣c,其中 a = n − m , b = L , c = x x − y y a=n-m,b=L,c=xx-yy a=n−m,b=L,c=xx−yy
-
若有解,可用扩展欧几里得求出满足 a x 0 + b y 0 = gcd ( a , b ) ax_0+by_0=\gcd(a,b) ax0+by0=gcd(a,b) 的解。然后满足 a x + b y = c ax+by=c ax+by=c 的解即为 x 1 = c gcd ( a , b ) x 0 x_1=\dfrac{c}{\gcd(a,b)}x_0 x1=gcd(a,b)cx0,则任意解为 x = x 1 + k b gcd ( a , b ) x=x_1+k\dfrac{b}{\gcd(a,b)} x=x1+kgcd(a,b)b
要求最小正数的特解,只需要 x = ( x % r + r ) % r , 其 中 r = b gcd ( a , b ) x=(x\%r+r)\%r,其中r=\dfrac{b}{\gcd(a,b)} x=(x%r+r)%r,其中r=gcd(a,b)b
#include<bits/stdc++.h> using namespace std; #define rep(i, a, b) for(int i = (a); i <= (b); i++) #define per(i, a, b) for(int i = (a); i >= (b); i--) #define ll long long ll x, y, m, n, L, xx, yy, a, c, g; ll exgcd(ll a, ll b, ll &x, ll &y){ //求出 ax + by = gcd(a,b) 的特解 if(b == 0){ x = 1, y = 0; return a; } ll gcd = exgcd(b, a % b, x, y); ll tmp = y; y = x - (a / b) * y; x = tmp; return gcd; } int main(){ cin >> xx >> yy >> m >> n >> L; m %= L, n %= L, xx %= L, yy %= L; a = n - m, c = xx - yy; if(a < 0) a *= -1, c *= -1; g = exgcd(a, L, x, y); if(c % g != 0){ puts("Impossible"); return 0; } x *= c / g; x = (x % (L / g) + (L / g)) % (L / g); cout << x << endl; }
-
逆元
-
单独求某个数逆元:扩展欧几里得 O ( log n ) O(\log n) O(logn)
-
统一求 1 ∼ n 1\sim n 1∼n 中每个数关于 p p p 的逆元:线性求逆元 O ( n ) O(n) O(n),单独求每个数花费约 O ( n 1 3 ) O(n^{1\over 3}) O(n31)
-
代码:
inv[1] = 1; for (int i = 2; i <= n; ++i) { inv[i] = (long long)(p - p / i) * inv[p % i] % p; }
-
证明:
首先 1 − 1 ≡ 1 ( m o d p ) 1^{-1}\equiv1\pmod p 1−1≡1(modp). 要求 i − 1 ( m o d p ) i^{-1}\pmod p i−1(modp)
令 k = p / i , r = p % i k=p/i,r=p\%i k=p/i,r=p%i,有 p = k i + r p=ki+r p=ki+r,则 k i + r ≡ 0 ( m o d p ) ki+r\equiv 0\pmod p ki+r≡0(modp)
两边同乘 i − 1 × r − 1 i^{-1}\times r^{-1} i−1×r−1: i − 1 ≡ − k r − 1 ( m o d p ) i^{-1}\equiv -kr^{-1}\pmod p i−1≡−kr−1(modp),即 i − 1 ≡ − ( p / i ) ∗ ( p % i ) − 1 ( m o d p ) \color{red}i^{-1}\equiv -(p/i)*(p\%i)^{-1}\pmod p i−1≡−(p/i)∗(p%i)−1(modp)
-