这是我在ACM竞赛中学习数论时整理的一些基础的知识点,主要讨论整除、同余、最大公约数以及素数及其判定、快速幂、逆元,写的不太专业,有错误的地方还请多批评指正!
整除
- 定义: 整数 a , b ∈ Z , 且 a ≠ 0 a, b\in\Z,且a\neq0 a,b∈Z,且a=0,若 ∃ q \exists q ∃q,使得 b = a q b=aq b=aq,则称b可被a整除(或a能整除b),记作 a ∣ b a\mid b a∣b;且称b是a的倍数,a是b的约数(或因子、因数)。若a不能整除b,则记作 a ∤ b a\nmid b a∤b
- 定理
- 1.
a
∣
b
,
且
b
∣
c
,
则
a
∣
c
a\mid b,且b\mid c, 则a\mid c
a∣b,且b∣c,则a∣c
- 证明: ∵ a ∣ b , 且 b ∣ c ; 令 b = p a , c = q b , ∴ c = p q a \because a\mid b,且b\mid c;令b=pa,c=qb,\therefore c=pqa ∵a∣b,且b∣c;令b=pa,c=qb,∴c=pqa,根据整除定义即 a ∣ c a\mid c a∣c
- 2.
a
∣
b
,
且
a
∣
c
,
则
∀
x
,
y
,
满
足
a
∣
b
x
+
c
y
a\mid b,且a\mid c,则\forall x,y,满足a\mid bx+cy
a∣b,且a∣c,则∀x,y,满足a∣bx+cy
- 证明: ∵ a ∣ b 且 a ∣ c , 令 b = p a , c = q a , 则 b x + c y = p a x + q a y = ( p x + q y ) a , ∴ a ∣ b x + c y \because a\mid b且a\mid c,令b=pa,c=qa,则bx+cy=pax+qay=(px+qy)a,\therefore a\mid bx+cy ∵a∣b且a∣c,令b=pa,c=qa,则bx+cy=pax+qay=(px+qy)a,∴a∣bx+cy
- 3. 若
a
≠
0
,
b
=
a
q
+
c
a\neq0,b=aq+c
a=0,b=aq+c,那么
a
∣
b
a\mid b
a∣b 的充分必要条件是
a
∣
c
a\mid c
a∣c
- 充分性证明: ∵ a ∣ b , 令 b = p a , 则 c = ( p − q ) a , ∴ a ∣ c \because a\mid b,令b=pa,则c=(p-q)a,\therefore a\mid c ∵a∣b,令b=pa,则c=(p−q)a,∴a∣c
- 必要性证明: ∵ a ∣ c , 令 c = p a , 则 b = ( p + q ) a , ∴ a ∣ b \because a\mid c,令c=pa,则b=(p+q)a,\therefore a\mid b ∵a∣c,令c=pa,则b=(p+q)a,∴a∣b
- 1.
a
∣
b
,
且
b
∣
c
,
则
a
∣
c
a\mid b,且b\mid c, 则a\mid c
a∣b,且b∣c,则a∣c
同余
∀ a , b ∈ Z , 且 a ≠ 0 , ∃ p , c \forall a,b\in\Z,且a\neq 0,\exists p,c ∀a,b∈Z,且a=0,∃p,c,使得等式 b = p a + c , 0 ⩽ c ⩽ a − 1 b=pa+c,0\leqslant c\leqslant a-1 b=pa+c,0⩽c⩽a−1成立
- 取模运算
b对a取模的结果即b除以a的余数,符号表示为 b m o d a b\mod a bmoda,计算值为 b − ⌊ b a ⌋ ∗ a b-\lfloor \frac{b}{a}\rfloor*a b−⌊ab⌋∗a。 - 同余
m ⩾ 1 , a , b ∈ Z , 若 m ∣ ( a − b ) m\geqslant 1,a,b\in\Z,若m\mid(a-b) m⩾1,a,b∈Z,若m∣(a−b),则称a与b关于模m同余,符号表示为 a ≡ b ( m o d m ) a\equiv b(mod\ m) a≡b(mod m)。 - 性质
- 1. 自反性: a ≡ a ( m o d m ) a\equiv a(mod\ m) a≡a(mod m)
- 2. 对称性: a ≡ b ( m o d m ) 则 b ≡ a ( m o d m ) a\equiv b(mod\ m)则b\equiv a(mod\ m) a≡b(mod m)则b≡a(mod m)
- 3. 传递性:
a
≡
b
(
m
o
d
m
)
且
b
≡
c
(
m
o
d
m
)
则
a
≡
c
(
m
o
d
m
)
a\equiv b(mod\ m)且b\equiv c(mod\ m)则a\equiv c(mod\ m)
a≡b(mod m)且b≡c(mod m)则a≡c(mod m)
- 证明: ∵ a ≡ b ( m o d m ) 且 b ≡ c ( m o d m ) \because a\equiv b(mod\ m)且b\equiv c(mod\ m) ∵a≡b(mod m)且b≡c(mod m)则根据同余定义 m ∣ ( a − b ) , m ∣ ( b − c ) m\mid (a-b),m\mid(b-c) m∣(a−b),m∣(b−c),再根据整除的定理2, m ∣ [ ( a − b ) + ( b − c ) ] , 即 m ∣ ( a − c ) m\mid[(a-b)+(b-c)],即m\mid(a-c) m∣[(a−b)+(b−c)],即m∣(a−c),再由同余定义得 a ≡ c ( m o d m ) a\equiv c(mod\ m) a≡c(mod m)
- 4. 同余式相加:
a
≡
b
(
m
o
d
m
)
且
c
≡
d
(
m
o
d
m
)
则
a
±
c
≡
b
±
d
(
m
o
d
m
)
a\equiv b(mod\ m)且c\equiv d(mod\ m)则a\pm c\equiv b\pm d(mod\ m)
a≡b(mod m)且c≡d(mod m)则a±c≡b±d(mod m)
- 证明: ∵ a ≡ b ( m o d m ) 且 c ≡ d ( m o d m ) ∴ m ∣ ( a − b ) , m ∣ ( c − d ) ∴ m ∣ [ ( a − b ) + ( c − d ) ] , m ∣ [ ( a + c ) − ( b + d ) ] ∴ a + c ≡ b + d ( m o d m ) \because a\equiv b(mod\ m)且c\equiv d(mod\ m)\therefore m\mid(a-b),m\mid(c-d)\therefore m\mid[(a-b)+(c-d)],m\mid[(a+c)-(b+d)]\therefore a+c\equiv b+d(mod\ m) ∵a≡b(mod m)且c≡d(mod m)∴m∣(a−b),m∣(c−d)∴m∣[(a−b)+(c−d)],m∣[(a+c)−(b+d)]∴a+c≡b+d(mod m)同理也能证明 a − c ≡ b − d ( m o d m ) a-c\equiv b- d(mod\ m) a−c≡b−d(mod m)
- 5. 同余式相乘:
a
≡
b
(
m
o
d
m
)
且
c
≡
d
(
m
o
d
m
)
则
a
c
≡
b
d
(
m
o
d
m
)
a\equiv b(mod\ m)且c\equiv d(mod\ m)则ac\equiv bd(mod\ m)
a≡b(mod m)且c≡d(mod m)则ac≡bd(mod m)
- 证明: ∵ a ≡ b ( m o d m ) 且 c ≡ d ( m o d m ) ∴ m ∣ ( a − b ) , m ∣ ( c − d ) ∴ m ∣ [ ( a − b ) c + ( c − d ) b ] ∴ m ∣ ( a c − b d ) ∴ a c ≡ b d ( m o d m ) \because a\equiv b(mod\ m)且c\equiv d(mod\ m)\therefore m\mid(a-b),m\mid(c-d)\therefore m\mid[(a-b)c+(c-d)b]\therefore m\mid(ac-bd) \therefore ac\equiv bd(mod\ m) ∵a≡b(mod m)且c≡d(mod m)∴m∣(a−b),m∣(c−d)∴m∣[(a−b)c+(c−d)b]∴m∣(ac−bd)∴ac≡bd(mod m)
- 6. 除法:
a
c
≡
b
c
(
m
o
d
m
)
则
a
≡
b
(
m
o
d
m
g
c
d
(
c
,
m
)
)
ac\equiv bc(mod\ m)则a\equiv b(mod \ \frac{m}{gcd(c, m)})
ac≡bc(mod m)则a≡b(mod gcd(c,m)m),
g
c
d
(
c
,
m
)
gcd(c, m)
gcd(c,m)就是c和m的最大公约数
- 证明: 令 d = g c d ( c , m ) d=gcd(c,m) d=gcd(c,m),则令 c = p d , m = q d c=pd,m=qd c=pd,m=qd,满足 g c d ( p , q ) = 1 gcd(p,q)=1 gcd(p,q)=1 ∵ a c ≡ b c ( m o d m ) ⇒ q d ∣ ( a − b ) p d ⇒ q ∣ ( a − b ) p ∵ g c d ( p , q ) = 1 ∴ q ∣ ( a − b ) ∴ a ≡ b ( m o d q ) \because ac\equiv bc(mod\ m)\Rightarrow qd\mid(a-b)pd\Rightarrow q\mid(a-b)p\because gcd(p, q)=1\therefore q\mid(a-b)\therefore a\equiv b(mod\ q) ∵ac≡bc(mod m)⇒qd∣(a−b)pd⇒q∣(a−b)p∵gcd(p,q)=1∴q∣(a−b)∴a≡b(mod q),q即 m g c d ( c , m ) \frac{m}{gcd(c, m)} gcd(c,m)m
- 7. 幂运算: a ≡ b ( m o d m ) 则 a n ≡ b n ( m o d m ) a\equiv b(mod\ m)则a^{n}\equiv b^{n}(mod\ m) a≡b(mod m)则an≡bn(mod m)
最大公约数
最大公约数(Greatest Common Divisor,简称GCD)
-
定义: a,b的公共的约数中最大的约数,记作 g c d ( a , b ) gcd(a,b) gcd(a,b), g c d ( a , b ) = m a x { d , d ∣ a 且 d ∣ b } gcd(a,b)=max \{ d, d\mid a且d\mid b\} gcd(a,b)=max{d,d∣a且d∣b}
-
性质:
- 1. g c d ( a , b ) = g c d ( b , a ) gcd(a,b)=gcd(b,a) gcd(a,b)=gcd(b,a)
- 2.
g
c
d
(
a
,
b
)
=
g
c
d
(
a
,
a
±
b
)
gcd(a,b)=gcd(a,a\pm b)
gcd(a,b)=gcd(a,a±b)
- 证明:请点击链接
- 3. g c d ( a , b ) = g c d ( a ± m b ) gcd(a,b)=gcd(a\pm mb) gcd(a,b)=gcd(a±mb)
- 4.
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
gcd(a,b)=gcd(b,a\%b)
gcd(a,b)=gcd(b,a%b)
- 证明: g c d ( b , a % b ) = g c d ( b , a − ⌊ a b ⌋ ∗ b ) gcd(b,a\%b)=gcd(b,a-\lfloor\frac{a}{b}\rfloor*b) gcd(b,a%b)=gcd(b,a−⌊ba⌋∗b),根据性质1、3可得gcd(a,b)=gcd(b,a%b)
- 5.
g
c
d
(
m
a
,
m
b
)
=
m
∗
g
c
d
(
a
,
b
)
gcd(ma,mb)=m*gcd(a,b)
gcd(ma,mb)=m∗gcd(a,b)
- 证明: 令 d = g c d ( a , b ) , 则 d ∣ a , d ∣ b , 满 足 a = p d , b = q d 且 g c d ( p , q ) = 1 ∴ g c d ( m a , m b ) = g c d ( m d p , m d q ) = m d d=gcd(a,b),则d|a,d|b,满足a=pd,b=qd且gcd(p,q)=1\therefore gcd(ma,mb)=gcd(mdp,mdq)=md d=gcd(a,b),则d∣a,d∣b,满足a=pd,b=qd且gcd(p,q)=1∴gcd(ma,mb)=gcd(mdp,mdq)=md
-
求解方法
辗转相除法(欧几里德法)- 原理:
g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)
当b=0时,gcd(a,b)=a,否则根据公式继续迭代。 - 代码实现:
int gcd(int a, int b){ return b == 0 ? a : gcd(b, a%b); }
- 原理:
-
扩展欧几里德:ax+by=gcd(a,b)
- 原理:
令 d = g c d ( a , b ) 令d=gcd(a,b) 令d=gcd(a,b)
∵ a x + b y = g c d ( a , b ) \because ax+by=gcd(a,b) ∵ax+by=gcd(a,b)
∴ b x ′ + ( a % b ) y ′ = g c d ( b , a % b ) \therefore bx^{'} +(a\%b)y^{'}=gcd(b,a\%b) ∴bx′+(a%b)y′=gcd(b,a%b)
∴ b x ′ + ( a % b ) y ′ = a x + b y \therefore bx^{'}+(a\%b)y^{'}=ax+by ∴bx′+(a%b)y′=ax+by
∴ a y ′ + b ( x ′ − ⌊ a b ⌋ ∗ y ′ ) = a x + b y \therefore ay^{'}+b(x^{'}-\lfloor\frac{a}{b}\rfloor*y^{'})=ax+by ∴ay′+b(x′−⌊ba⌋∗y′)=ax+by要使任意a,b都满足此等式,
则 x = y ′ , y = x ′ − ⌊ a b ⌋ ∗ y ′ x=y^{'},y=x^{'}-\lfloor\frac{a}{b}\rfloor*y^{'} x=y′,y=x′−⌊ba⌋∗y′
当b=0时,ax=a,则x=1,y=0;
否则,令用 g c d ( a , b ) = g c d ( b , a % b ) gcd(a,b)=gcd(b,a\%b) gcd(a,b)=gcd(b,a%b)递归求解,回溯时利用两式之间的关系,更新x,y,可求得方程的一组整数解。 - 代码实现:
int exgcd(int a, int b, int &x, int &y){ if(!b) {x = 1; y = 0; return a;} int d = exgcd(b, a % b, x, y); int z = x; x = y; y = z - (a / b) * y; return d; }
- 方程
a
x
+
b
x
=
c
ax+bx=c
ax+bx=c:
利用扩展欧几里德算法可求得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的一组解 x 0 , y 0 x_0,y_0 x0,y0;令d=gcd(a,b)
+ 当 d ∣ c d\mid c d∣c时,方程有解,其一组解为:
x = x 0 ∗ c d , y = y 0 ∗ c d x=x_0*\frac{c}{d},y=y_0*\frac{c}{d} x=x0∗dc,y=y0∗dc
通解为:
x = x 0 ∗ c d + k ∗ b d x=x_0*\frac{c}{d}+k*\frac{b}{d} x=x0∗dc+k∗db
y = y 0 ∗ c d + k ∗ a d , ( k ∈ Z ) y=y_0*\frac{c}{d}+k*\frac{a}{d},(k\in\Z) y=y0∗dc+k∗da,(k∈Z)。
- 原理:
扩展欧几里德例题
- POJ1061
- 题意: 两只青蛙在同一条维度线上朝西跳动, 青蛙A起始点坐标x,每次跳动m米,青蛙B起始点坐标y,每次跳动n米,问他们跳动多少次能沟相遇,不能输出Impossible
- 题解: 设两只青蛙跳动次数为t,则此时青蛙A的坐标为(x+mt)%l,青蛙B的坐标为(y+nt)%l,当两只青蛙相遇时,满足
( x + m t ) ≡ ( y + n t ) ( m o d l ) (x+mt)\equiv(y+nt)(\mod l) (x+mt)≡(y+nt)(modl)
l ∣ ( ( x + m t ) − ( y + n t ) ) l\mid((x+mt)-(y+nt)) l∣((x+mt)−(y+nt))
∃ k ∈ Z \exists k\in\Z ∃k∈Z使得等式 ( ( x + m t ) − ( y + n t ) ) = k l ((x+mt)-(y+nt))=kl ((x+mt)−(y+nt))=kl成立
( m − n ) t + l k = x − y (m-n)t+lk=x-y (m−n)t+lk=x−y
当 g c d ( m − n , l ) ∣ ( x − y ) gcd(m-n,l)\mid(x-y) gcd(m−n,l)∣(x−y)时,方程有解,即答案就是t的最小正整数解,否则方程无解,则两青蛙永远不能相遇。 - 代码实现:
#include <iostream> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; #define mes(a, val) memset(a, val, sizeof a) #define mec(b, a) memcpy(b, a, sizeof a) #define ll long long ll exgcd(ll a, ll b, ll &x, ll &y){ if(!b) {x = 1; y = 0; return a;} ll d = exgcd(b, a % b, x, y); ll z = x; x = y; y = z - (a / b) * y; return d; } int main() { ll n, m, x, y, l; scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &l); ll a = n - m; ll b = l; ll c = x - y; ll d = exgcd(a, b, x, y); if(d < 0) d = -d; if(c % d){ printf("Impossible\n"); } else { ll p = b / d; x = x * c / d; x = ((x % p) + p) % p; printf("%lld\n", x); } return 0; }
最小公倍数
最小公倍数(Least Common Multiple 简称LCM )
- 定义: a, b的所有公共的倍数中最小的公倍数,记作lcm(a,b), l c m ( a , b ) = m i n { d , a ∣ d 且 b ∣ d } lcm(a,b)=min\{d,a\mid d且b\mid d\} lcm(a,b)=min{d,a∣d且b∣d}。
- 计算方法: 公式 l c m ( a , b ) = a ∗ b g c d ( a , b ) lcm(a,b)=\frac{a*b}{gcd(a,b)} lcm(a,b)=gcd(a,b)a∗b
素数及其判定
- 素数(也称质数): 一个正整数n是素数当且仅当只能被1和其自身整除,否则就称n是合数 。
注: 1既不是素数也不是合数,最小的素数是2 - 素数判定
- 1. 暴力枚举:
- 原理: 循环遍历i从2到 n \sqrt{n} n,只要存在 n % i = 0 n\%i=0 n%i=0即可判定n不是素数,这样每次判定时间复杂度都是 O ( n ) O(\sqrt{n}) O(n)
- 代码实现
bool isprime(int n){ for(int i = 2; i * i <= n; i ++){ if(n % i == 0) return false; } return true; }
- 2. 埃拉托斯特尼筛法,简称埃氏筛:
是一种用来求自然数n以内的全部素数的方法 。- 原理: 一个合数必然可以表示一个质数和另一个数相乘。对于一个素数p,那么p的倍数2p,3p,…kp,…必然都是合数。时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
- 实现代码:
/* 定义数组:isprime[i],表示数组i是否时素数 */ const int maxn = 1e5 + 100; int m; bool isprime[maxn]; int p[maxn]; void sieze(int n) { m = 0; memset(isprime, true, sizeof(isprime)); for(int i = 2; i <= n; i ++){ if(isprime[i]){///表明 i 就是素数 p[++ m] = i; for(int j = 2 * i; j <= n; j += i){ isprime[j] = false; } } } }
- 不足: 仔细分析可以看出,这种方法筛出n以内的所有素数还是有不足之处的。原因在于每个合数会被其每一个质因子筛到一次。因此就有了接下来的线性筛。
- 3. 线性筛
它能在 O ( n ) O(n) O(n)的时间复杂度内筛选出n以内的所有素数。- 原理:
线性筛能保证n以内的任何一个数只能被他的最小质因子筛一次。
一个数n可以表示成 n = F a c t o r y m a x ∗ p n=Factory_{max}*p n=Factorymax∗p
F a c t o r y m a x Factory_{max} Factorymax是除n以外的最大因子,p是n的质因子,满足p小于等于 F a c t o r y m a x Factory_{max} Factorymax 的所有质因子。
用数组v[i]表示i是否是素数,v[i]为false时表示i是素数,
然后便利之前筛出的所有素数,v[i*p[j]]则不是素数,标记为true。 i % p[j] 为0时,跳出第二层循环,以此保证每个数只被他的最小质因子筛一次。
当i %p[j]为0时,p[j]已经是i的最小值质因子,且p[j]时i*p[j]的最小值质因子,p[j+1]大于i的最小质因子,也就不是i*p[j]的最小质因子。 - 代码实现:
const int maxn = 1e5 + 100; int m; bool v[maxn]; int p[maxn]; void sieze(int n) { m = 0; memset(v, false, sizeof v); for(int i = 2; i <= n; i ++){ if(!v[i]) { p[++ m] = i; } for(int j = 1; j <= m && i * p[j] <= n; j ++){ v[i * p[j]] = true; if(i % p[j] == 0) break;//这条语句很关键 } } }
- 原理:
- 1. 暴力枚举:
整除分块
计算表达式: ∑ i = 1 n ⌊ n i ⌋ \sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor ∑i=1n⌊in⌋
- 1.暴力O(n)求解不多赘述。
- 2.
O
n
O\sqrt{n}
On优美解法
- 证明请看,这位大佬的证明非常清晰,很容易理解。
- 代码实现
int solve(int n) { int ans = 0; for(int l = 1, r; l <= n; l = r + 1){ r = n / (n / l); ans += (r - l + 1) * (n / l); } return ans; }
- 例题Gym100923I
- 题意: 有n个男的,n个女的,第i个人都有为当前一个大小为i的懒惰值,当一男一女懒惰值的乘积<=n他们就就可以一起跳舞,请问有多少种组合可能;
- 题解: 从1~n枚举男生的懒惰值i,满足条件的女女生个数就是 ⌊ n i ⌋ \lfloor\frac{n}{i}\rfloor ⌊in⌋, a n s = ∑ i = 1 n ⌊ n i ⌋ ans=\sum_{i=1}^{n}\lfloor\frac{n}{i}\rfloor ans=∑i=1n⌊in⌋,经典的整除分块例题。
- 代码实现:
#include <bits/stdc++.h> using namespace std; #define mes(a, val) memset(a, val, sizeof a) #define mec(b, a) memcpy(b, a, sizeof a) #define ll long long ll solve(ll n) { ll ans = 0; for(ll l = 1, r; l <= n; l = r + 1){ r = n / (n / l); ans += (r - l + 1) * (n / l); } return ans; } int main() { freopen("perechi3.in", "r", stdin); freopen("perechi3.out", "w", stdout); int T; scanf("%d", &T); while(T --){ ll n; scanf("%lld", &n); ll ans = solve(n); printf("%lld\n", ans); } return 0; }
快速幂
- 问题描述: 计算 a n a^{n} an为例
- 问题分析: 显然 O ( n ) O(n) O(n)暴力是不能解决问题的,下面我们介绍一种算法 (快速幂) ,它能在 O ( l o g n ) O(log n) O(logn)时间复杂度内解决此问题。
- 快速幂原理:
将正整数n二进制拆分成 n = b n − 1 b n − 2 . . . b 0 , b i ∈ { 0 , 1 } n=b_{n-1}b_{n-2}...b_{0},b_{i}\in\{0,1\} n=bn−1bn−2...b0,bi∈{0,1}
即 n = 2 n − 1 b n − 1 + 2 n − 2 b n − 2 + . . . + 2 0 b 0 n=2^{n-1}b_{n-1}+2^{n-2}b_{n-2}+...+2^{0}b_{0} n=2n−1bn−1+2n−2bn−2+...+20b0
∴ a n = a 2 n − 1 b n − 1 + 2 n − 2 b n − 2 + . . . + 2 0 b 0 \therefore a^{n}=a^{2^{n-1}b_{n-1}+2^{n-2}b_{n-2}+...+2^{0}b_{0}} ∴an=a2n−1bn−1+2n−2bn−2+...+20b0
= a 2 n − 1 b n − 1 ∗ a 2 n − 2 b n − 2 ∗ . . . ∗ a 2 0 b 0 \quad\quad=a^{2^{n-1}b_{n-1}}*a^{2^{n-2}b_{n-2}}*...*a^{2^{0}b_{0}} =a2n−1bn−1∗a2n−2bn−2∗...∗a20b0
因此我们可以设置一个res(初始为1)和一个base(初始值为a),也即a^{0},在对n进行二进制拆分过程中,当第i位(从右往左第i位)为1时,则将res乘上base,并且base每次都乘上自己。其实在整个过程中, b a s e i = a 2 i base_{i}=a^{2^{i}} basei=a2i。时间复杂度 O ( l o g ( n ) ) O(log(n)) O(log(n))int ksm(int a, int n, int mod) { int res = 1; while(n){ if(n & 1) res = res * a % mod; //二进制拆分n第i位(从右往左)为1 a = a * a % mod; n >>= 1; } return res; }
逆元
- 定义: 整数a,b,若
a
∗
b
≡
1
(
m
o
d
p
)
a*b\equiv1(\mod p)
a∗b≡1(modp),那么则称a和b互为模p意义下的逆元。
- 注:逆元需要在取模下才有意义,a对模p意义下的逆元即a在模p意义下的倒数
- 逆元的意义: 如何求解
a
b
%
p
\frac{a}{b}\% p
ba%p(p是质数)?
整数的除法运算是向下取整,若b不能整除a,进行除法运算后在对p取模,多得到的并是正确结果,这一点应该很好理解。
逆元的作用就在如此,他能将除以一个数等价于乘上这个的逆元。
a b ≡ a ∗ 1 b ( m o d p ) \frac{a}{b}\equiv a*\frac{1}{b}(\mod p) ba≡a∗b1(modp)
设b在模p意义下的逆元为invb,则
i n v b ∗ b ≡ 1 ( m o d p ) ⇔ i n v b ≡ 1 b ( m o d p ) invb*b\equiv1(\mod p)\Leftrightarrow invb\equiv\frac{1}{b}(\mod p) invb∗b≡1(modp)⇔invb≡b1(modp)
∴ a b ≡ a ∗ i n v b ( m o d p ) \therefore \frac{a}{b}\equiv a*invb(\mod p) ∴ba≡a∗invb(modp) - 逆元求解方法
- 1.费马小定理
- 满足条件: 模数p必须是素数。
- 原理:
a
p
−
1
≡
1
(
m
o
d
p
)
a^{p-1}\equiv1(\mod p)
ap−1≡1(modp),p是素数 (再此不做证明)
因此 a ∗ a p − 2 ≡ 1 ( m o d p ) a*a^{p-2}\equiv1(\mod p) a∗ap−2≡1(modp)
即 a p − 2 a^{p-2} ap−2就是a在模p意义下的逆元, i n v a = a p − 2 inva=a^{p-2} inva=ap−2,利用快速幂可以求解
- 2.扩展欧几里德
- 满足条件: g c d ( a , p ) = 1 gcd(a,p)=1 gcd(a,p)=1即a与p互质。
- 原理:
i
n
v
a
∗
a
≡
1
(
m
o
d
p
)
inva *a\equiv1(\mod p)
inva∗a≡1(modp)
∴ p ∣ ( i n v a ∗ a − 1 ) \therefore p\mid(inva*a-1) ∴p∣(inva∗a−1)
∴ ∃ k ∈ Z , i n v a ∗ a − 1 = p k \therefore \exists k\in\Z,inva*a-1=pk ∴∃k∈Z,inva∗a−1=pk
∴ i n v a ∗ a + k ∗ p = 1 , ∵ g c d ( a , p ) = 1 \therefore inva*a+k*p=1,\because gcd(a,p)=1 ∴inva∗a+k∗p=1,∵gcd(a,p)=1
因此次方程一定有解,利用扩展欧几里德算法可求得inva
- 递推公式
- 满足条件: 模数p必定是素数 。
- 原理:
p = p % a + ⌊ p a ⌋ ∗ a p=p\%a+\lfloor\frac{p}{a}\rfloor*a p=p%a+⌊ap⌋∗a
令 x = p % a , y = ⌊ p a ⌋ x=p\%a,y=\lfloor\frac{p}{a}\rfloor x=p%a,y=⌊ap⌋
p % p = ( x + y ∗ a ) % p p\%p=(x+y*a)\%p p%p=(x+y∗a)%p
a − 1 % p = ( p − x ) ∗ y − 1 % p a^{-1}\%p=(p-x)*y^{-1}\%p a−1%p=(p−x)∗y−1%p
因此得 a − 1 = ( p − ⌊ p a ⌋ ) ∗ ( p % a ) − 1 % p a^{-1}=(p- \lfloor\frac{p}{a}\rfloor)*(p\% a)^{-1}\%p a−1=(p−⌊ap⌋)∗(p%a)−1%p
由此递推式可O(n)求解1~n所有数模p的逆元。 - 代码实现:
int inv[mod+5]; void getInv(int p) { inv[1] = 1; for(int i = 2; i < p; i ++){ inv[i] = (p - p / i) * inv[p % i] % p; } }
- 1.费马小定理
O(n)计算阶乘的逆元
- 原理:
令 f ( n ) = n ! f(n)=n! f(n)=n!
f ( n ) = f ( n − 1 ) ∗ n f(n)=f(n-1)*n f(n)=f(n−1)∗n
f ( n − 1 ) − 1 = f ( n ) − 1 ∗ n f(n-1)^{-1}=f(n)^{-1}*n f(n−1)−1=f(n)−1∗n
先 O ( n ) O(n) O(n) 预处理出 1~n 的阶乘,计算出 f ( n ) − 1 f(n)^{-1} f(n)−1 则可通过递推公式往下求解 1~n-1 以内所有阶乘的逆元。 - 代码实现:
const int maxn = 1e5 + 10; int f[maxn], invf[maxn]; int p; int ksm(int a, int n, int p) { int res = 1; while(n){ if(n & 1) res = 1ll * res * a % p; a = 1ll * a * a % p; n >>= 1; } return res; } void init(int n) { f[0] = 1; f[1] = 1; for(int i = 2; i <= n; i ++){ f[i] = 1ll * f[i-1] * i % p; } invf[n] = ksm(f[n], p - 2, p); for(int i = n - 1; i >= 0; i --){ invf[i] = 1ll * invf[i+1] * (i + 1) % p; } }
- 例题
51Nod1118- 题意:
M * N的方格,一个机器人从左上走到右下,只能向右或向下走。有多少种不同的走法?由于方法数量可能很大,只需要输出Mod 10^9 + 7的结果。 - 题解: 答案就是 C n + m n C_{n+m}^{n} Cn+mn
- 代码实现:
import java.math.*; import java.util.*; public class Main { public static long mod = 1000000007; public static long f[] = new long[2100]; public static long invf[] = new long[2100]; public static long ksm(long a, long n, long p) { long res = 1; while(n > 0) { if(n % 2 == 1) res = res * a % p; a = a * a % p; n >>= 1; } return res; } //计算阶乘的逆元 public static void init(int n) { f[0] = 1; for(int i = 1; i <= n; i ++) { f[i] = f[i-1] * i % mod; } invf[n] = ksm(f[n], mod-2, mod); for(int i = n - 1; i >= 0; i --) { invf[i] = invf[i+1] * (i + 1) % mod; } } public static long get(int n, int m) { n --; m --; long res = f[n+m]*invf[n]%mod*invf[m]%mod; res = res % mod; return res; } public static void main(String[] args) { Scanner cin = new Scanner(System.in); init(2005); int n = cin.nextInt(); int m = cin.nextInt(); System.out.println(get(n, m)); } }
- 题意: