【数学】同余基本概念与快速幂

同余基本概念与快速幂

基本概念与性质

  • 定义:ab(modm)a\equiv b(\mod m)
  • 定义的等价条件:m(ab)kN,a=b+kmm\mid (a-b)\Leftrightarrow \exist k\in N,a=b+km
  • cN,a±cb±c(modm),acbc(modm)\Rightarrow c\in N,a\pm c\equiv b\pm c(\mod m),ac\equiv bc(\mod m)
  • anbn,n>0\Rightarrow a^n\equiv b^n,n>0
  • f(a)f(b),f(x)整系数多项式\Rightarrow f(a)\equiv f(b),f(x)整系数多项式
  • gcd(a,m)=gcd(b,m)\Rightarrow\gcd(a,m)=\gcd(b,m)
  • ,dmab(modd),d\mid m\Rightarrow a\equiv b(\mod d)
  • 同余是等价关系(自反、对称、传递)
  • mm 剩余类:将整数分成mm 个集合使得任意两个整数模mm 同余
  • mm 完全剩余系:从模mm 的每个剩余类中选一个数,构成的mm 个数的集合
  • ab(modm),cd(modm)ax+cybx+dy(modm),x,yN,acbd(modm)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)
  • acbc(modm),gcd(c,m)=dab(modm/d)ac\equiv bc(\mod m),\gcd(c,m)=d\Rightarrow a\equiv b(\mod m/d)
  • ab(a%m)(b%m)(modm)ab\equiv(a\%m)(b\%m)(\mod m)

乘法改加法

防止乘法溢出的方法,原理跟计组里的乘法器很像:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 计算a*b mod m,防止溢出
// 其中里面的取模为了更快还可以改成if(ans > m) ans -= m;
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,ax1(modm)\gcd(a,m)=1,ax\equiv 1(\mod m)xx 的解即为aa 的逆。

分数逆元:abmodm=ab1modm\frac{a}{b}\mod m=a\cdot b^{-1} \mod m

方法一:费马小定理

定理:gcd(a,m)=1,m为素数am11(modm)aam21(modm)\gcd(a,m)=1,m为素数\Rightarrow a^{m-1}\equiv 1(\mod m)\Leftrightarrow a\cdot a^{m-2}\equiv 1(\mod m)

用快速幂求ap2a^{p-2} 即可。

1
inline ll inv(int a) { return qpow(a, m - 2, m); }

方法二:拓展欧几里得

ainv(a)1(modm)a\cdot inv(a)\equiv 1(\mod m) ,拓展欧几里得解出ax+my=1ax+my=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; // 不互质无解
}

方法三:线性递推

mm 表示为m=kx+ym=kx+y ,其中k=m/x,y=m%xk=\lfloor m/x\rfloor,y=m\%x 。则kx+y0(modm)kx+y\equiv 0(\mod m) ,同乘inv(x)inv(y)inv(x)inv(y) ,则有kinv(y)+inv(x)0(modm)k\cdot inv(y)+inv(x)\equiv 0(\mod m) ,得inv(x)kinv(y)(modm)inv(x)\equiv -k\cdot inv(y)(\mod m)

inv(x)=kinv(y)=m/xinv(m%x)modminv(x)=-k\cdot inv(y)=-\lfloor m/x\rfloor\cdot inv(m\%x)\mod m ,即一个数的逆元可由一个比它小的数的推出来。把负数消掉即可。

注:mm 可以不是素数

1
2
3
4
5
6
7
// inv[1...last]是1...last的逆元
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
// fac[i]=i!, inv[i]=inv(i!)
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;
}

快速幂

普通快速幂

形式:xnmodmx^n \mod m

原理:将nn 拆成二进制,原式变为xn0×20+n1×21++nk×2k=xn0×20xn1×21xnk×2k=(x20)n0(x21)n1(x2k)nkx^{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} ,每次计算都取模,结果不变。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 快速幂:计算x^n mod m
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×nn\times n 的矩阵AAkk 次方:AkA^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;
}
}