同余基本概念与快速幂
基本概念与性质
定义:a ≡ b ( m o d m ) a\equiv b(\mod m) a ≡ b ( m o d m )
定义的等价条件:m ∣ ( a − b ) ⇔ ∃ k ∈ N , a = b + k m m\mid (a-b)\Leftrightarrow \exist k\in N,a=b+km m ∣ ( a − b ) ⇔ ∃ k ∈ N , a = b + k m
⇒ c ∈ N , a ± c ≡ b ± c ( m o d m ) , a c ≡ b c ( m o d m ) \Rightarrow c\in N,a\pm c\equiv b\pm c(\mod m),ac\equiv bc(\mod m) ⇒ c ∈ N , a ± c ≡ b ± c ( m o d m ) , a c ≡ b c ( m o d m )
⇒ a n ≡ b n , n > 0 \Rightarrow a^n\equiv b^n,n>0 ⇒ a n ≡ b n , n > 0
⇒ f ( a ) ≡ f ( b ) , f ( x ) 整系数多项式 \Rightarrow f(a)\equiv f(b),f(x)整系数多项式 ⇒ f ( a ) ≡ f ( b ) , f ( x ) 整 系 数 多 项 式
⇒ gcd ( a , m ) = gcd ( b , m ) \Rightarrow\gcd(a,m)=\gcd(b,m) ⇒ g cd( a , m ) = g cd( b , m )
, d ∣ m ⇒ a ≡ b ( m o d d ) ,d\mid m\Rightarrow a\equiv b(\mod d) , d ∣ m ⇒ a ≡ b ( m o d d )
同余是等价关系(自反、对称、传递)
模m m m 剩余类:将整数分成m m m 个集合使得任意两个整数模m m m 同余
模m m m 完全剩余系:从模m m m 的每个剩余类中选一个数,构成的m m m 个数的集合
a ≡ b ( m o d m ) , c ≡ d ( m o d m ) ⇒ a x + c y ≡ b x + d y ( m o d m ) , x , y ∈ N , a c ≡ b d ( m o d m ) a\equiv b(\mod m),c\equiv d(\mod m)\Rightarrow ax+cy\equiv bx+dy(\mod m),x,y\in N,ac\equiv bd(\mod m) a ≡ b ( m o d m ) , c ≡ d ( m o d m ) ⇒ a x + c y ≡ b x + d y ( m o d m ) , x , y ∈ N , a c ≡ b d ( m o d m )
a c ≡ b c ( m o d m ) , gcd ( c , m ) = d ⇒ a ≡ b ( m o d m / d ) ac\equiv bc(\mod m),\gcd(c,m)=d\Rightarrow a\equiv b(\mod m/d) a c ≡ b c ( m o d m ) , g cd( c , m ) = d ⇒ a ≡ b ( m o d m / d )
a b ≡ ( a % m ) ( b % m ) ( m o d m ) ab\equiv(a\%m)(b\%m)(\mod m) a b ≡ ( a % m ) ( b % m ) ( m o d m )
乘法改加法
防止乘法溢出的方法,原理跟计组里的乘法器很像:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ll multi_add (ll a, ll b, ll m) { a %= m; b %= m; ll ans = 0 ; while (b > 0 ) { if (b & 1 ) { ans += a; if (ans > m) ans -= m; } b >>= 1 ; a <<= 1 ; if (a > m) a -= m; } return ans; }
逆元
定义:gcd ( a , m ) = 1 , a x ≡ 1 ( m o d m ) \gcd(a,m)=1,ax\equiv 1(\mod m) g cd( a , m ) = 1 , a x ≡ 1 ( m o d m ) ,x x x 的解即为a a a 的逆。
分数逆元:a b m o d m = a ⋅ b − 1 m o d m \frac{a}{b}\mod m=a\cdot b^{-1} \mod m b a m o d m = a ⋅ b − 1 m o d m
方法一:费马小定理
定理:gcd ( a , m ) = 1 , m 为素数 ⇒ a m − 1 ≡ 1 ( m o d m ) ⇔ a ⋅ a m − 2 ≡ 1 ( m o d m ) \gcd(a,m)=1,m为素数\Rightarrow a^{m-1}\equiv 1(\mod m)\Leftrightarrow a\cdot a^{m-2}\equiv 1(\mod m) g cd( a , m ) = 1 , m 为 素 数 ⇒ a m − 1 ≡ 1 ( m o d m ) ⇔ a ⋅ a m − 2 ≡ 1 ( m o d m )
用快速幂求a p − 2 a^{p-2} a p − 2 即可。
1 inline ll inv (int a) { return qpow (a, m - 2 , m); }
方法二:拓展欧几里得
a ⋅ i n v ( a ) ≡ 1 ( m o d m ) a\cdot inv(a)\equiv 1(\mod m) a ⋅ i n v ( a ) ≡ 1 ( m o d m ) ,拓展欧几里得解出a x + m y = 1 ax+my=1 a x + m y = 1 答案,注意防止负数即可。
1 2 3 4 5 6 inline ll inv (ll a) { ll x, y, d; exgcd (a, m, d, x, y); return d == 1 ? (x % m + m) % m : -1 ; }
方法三:线性递推
将m m m 表示为m = k x + y m=kx+y m = k x + y ,其中k = ⌊ m / x ⌋ , y = m % x k=\lfloor m/x\rfloor,y=m\%x k = ⌊ m / x ⌋ , y = m % x 。则k x + y ≡ 0 ( m o d m ) kx+y\equiv 0(\mod m) k x + y ≡ 0 ( m o d m ) ,同乘i n v ( x ) i n v ( y ) inv(x)inv(y) i n v ( x ) i n v ( y ) ,则有k ⋅ i n v ( y ) + i n v ( x ) ≡ 0 ( m o d m ) k\cdot inv(y)+inv(x)\equiv 0(\mod m) k ⋅ i n v ( y ) + i n v ( x ) ≡ 0 ( m o d m ) ,得i n v ( x ) ≡ − k ⋅ i n v ( y ) ( m o d m ) inv(x)\equiv -k\cdot inv(y)(\mod m) i n v ( x ) ≡ − k ⋅ i n v ( y ) ( m o d m )
令i n v ( x ) = − k ⋅ i n v ( y ) = − ⌊ m / x ⌋ ⋅ i n v ( m % x ) m o d m inv(x)=-k\cdot inv(y)=-\lfloor m/x\rfloor\cdot inv(m\%x)\mod m i n v ( x ) = − k ⋅ i n v ( y ) = − ⌊ m / x ⌋ ⋅ i n v ( m % x ) m o d m ,即一个数的逆元可由一个比它小的数的推出来。把负数消掉即可。
注:m m m 可以不是素数
1 2 3 4 5 6 7 inline void init (ll inv[], ll last, ll m) { inv[1 ] = 1 ; for (ll i = 2ll ; i <= last;i++) inv[i] = (m - m / i) * inv[m % i] % m; }
阶乘逆元
1 2 3 4 5 6 7 8 9 10 11 int fac[N],inv[N];void init (int n) { fac[0 ]=1 ; for (int i=1 ;i<=n;i++) fac[i]=1ll *fac[i-1 ]*i%mod; inv[n]=qpow (fac[n],mod-2 ); for (int i=n-1 ;i>=0 ;i--) inv[i]=1ll *inv[i+1 ]*(i+1 )%mod; }
快速幂
普通快速幂
形式:x n m o d m x^n \mod m x n m o d m
原理:将n n n 拆成二进制,原式变为x n 0 × 2 0 + n 1 × 2 1 + ⋯ + n k × 2 k = x n 0 × 2 0 ⋅ x n 1 × 2 1 ⋯ x n k × 2 k = ( x 2 0 ) n 0 ⋅ ( x 2 1 ) n 1 ⋯ ( x 2 k ) n k x^{n_0\times 2^{0}+n_1\times 2^{1}+\cdots+n_k\times 2^{k}}=x^{n_0\times 2^{0}}\cdot x^{n_1\times 2^{1}}\cdots x^{n_k\times 2^{k}}=(x^{2^{0}})^{n_0}\cdot(x^{2^{1}})^{n_1}\cdots(x^{2^{k}})^{n_k} x n 0 × 2 0 + n 1 × 2 1 + ⋯ + n k × 2 k = x n 0 × 2 0 ⋅ x n 1 × 2 1 ⋯ x n k × 2 k = ( x 2 0 ) n 0 ⋅ ( x 2 1 ) n 1 ⋯ ( x 2 k ) n k ,每次计算都取模,结果不变。
1 2 3 4 5 6 7 8 9 10 11 12 13 ll qpow (ll x, ll n, ll m) { ll ans = 1ll ; while (n) { if (n & 1 ) ans = ans * x % m; x = x * x % m; n >>= 1 ; } return ans; }
矩阵快速幂
形式:n × n n\times n n × n 的矩阵A A A 的k k k 次方:A k A^k A k
与普通快速幂原理一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 struct matrix { ll a[N][N]; ll m; int n; matrix (int x = 0 , ll mod) : n (x), m (mod) {} void build () { scanf ("%d" , &n); scanf ("%lld" , &m); for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) scanf ("%lld" , &a[i][j]); } void init () { for (int i = 1 ; i <= n; i++) a[i][i] = 1ll ; } matrix operator *(const matrix &b) { matrix nxt; for (int k = 1 ; k <= n; k++) for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) nxt.a[i][j] = (nxt.a[i][j] + a[i][k] % m * b.a[k][j] % m) % m; return nxt; } }; matrix matrix_qpow (matrix A, ll k) { matrix ans (A.n, A.m) ; ans.init (); while (k) { if (k & 1 ) ans = ans * A; A = A * A; k >>= 1ll ; } }