质数筛法
Eratosthenes筛选法
顾名思义就是数学家 Eratosthenes 发明的筛法,简称为埃氏筛。
基本思想
质数的倍数一定不是质数。
实现方法
用一个长度为 N + 1 N+1 N+1 的数组保存信息, 0 0 0 表示质数, 1 1 1 表示合数。先假设所有的数都是质数(初始化为 0 0 0),从小到大枚举每一个质数 x x x,把 x x x 的倍数都标记为非质数(置为 1 1 1)。
如何枚举质数 x x x 呢?从小到大扫描到 x x x 时,若 x x x 未被标记,则它不能被 2 ∼ x − 1 2\sim x-1 2∼x−1 之间的任何数整除,则 x x x 为质数。
举个例子:
2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , ⋯ ⇓ 2 , 3 , 5 , 7 , 11 , ⋯ ⇓ ⋯ \begin{aligned} &2,3,4,5,6,7,8,9,10,11,12,\cdots\\ &\Downarrow\\ &2,3,5,7,11,\cdots\\ &\Downarrow\\ &\cdots \end{aligned} 2,3,4,5,6,7,8,9,10,11,12,⋯⇓2,3,5,7,11,⋯⇓⋯
可以发现,存在重复标记的耗时行为。实际上,小于 x 2 x^2 x2 的 x x x 的倍数在扫描更小的数时就已经被标记过了。因此,可以优化一下,对于每个 x x x,把大于等于 x 2 x^2 x2 的 x x x 的倍数标记为合数即可。
代码如下:
void primes(int n)
{
memset(v,0,sizeof(v));
for(int i=2;i<=n;i++)
{
if(v[i]) continue;
cout<<i<<endl;
for(int j=i;j<=n/i;j++) v[i*j]=1;
}
}
算法的时间复杂度为
O
(
n
log
log
n
)
O(n\log\log n)
O(nloglogn),效率非常接近于线性。时间复杂度的证明非常复杂,本蒟蒻也不太会,所以不证了
线性筛法
即使在优化后,埃氏筛仍然会重复标记合数。举个栗子, 12 12 12 既会被 2 2 2 标记又会被 3 3 3 标记,其根本原因是算法不能确定唯一的产生 12 12 12 的方式。
所以,我们只要保证合数被它的最小质因数筛去就好啦!时间复杂度为 O ( n ) O(n) O(n)。
代码如下:
int v[maxn],prime[maxn];
void primes(int n)
{
memset(v,0,sizeof(v));//存储最小质因子
int m=0;//质数数量
for(int i=2;i<=n;i++)
{
if(v[i]==0)
v[i]=i,prime[++m]=i;
for(int j=1;j<=m;j++)
{
//i有比prime[j]更小的质因子或者要筛的数超出n的范围
if(prime[j]>v[i]||prime[j]*i>n) break;
v[i*prime[j]]=prime[j];
}
}
for(int i=1;i<=m;i++)
cout<<prime[i]<<endl;
}
费马小定理、欧拉定理
费马小定理
如果 p p p 是一个质数,而整数 a a a 不是 p p p 的倍数,则有 a p − 1 = 1 ( m o d p ) a^{p-1}=1\pmod p ap−1=1(modp)。
一般情况: a p = a ( m o d p ) a^p=a \pmod p ap=a(modp)
欧拉定理
欧拉函数
对正整数 n n n,欧拉函数是小于等于 n n n 的数中与 n n n 互质的数的数目。
引理 1
- 如果 n n n 为某一个素数 p p p,则: φ ( p ) = p − 1 \varphi(p)=p-1 φ(p)=p−1;
- 如果 n n n 为某一个素数的 p p p 的幂次 p n p^n pn,则: φ ( p n ) = ( p − 1 ) × p n − 1 \varphi(p^n)=(p-1) \times p^{n-1} φ(pn)=(p−1)×pn−1;
- 如果 n n n 为任意两个互质的数 a , b a,b a,b 的积,则: φ ( a × b ) = φ ( a ) φ ( b ) \varphi(a\times b)=\varphi(a)\varphi(b) φ(a×b)=φ(a)φ(b)。
引理 2
设 n = p 1 a 1 × p 2 a 2 × ⋅ ⋅ ⋅ × p k a k n=p_1^{a_1}\times p_2^{a_2}\times···\times p_k^{a_k} n=p1a1×p2a2×⋅⋅⋅×pkak 为正整数 n n n 的素数幂乘积表达式,则: φ ( n ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × ⋅ ⋅ ⋅ × ( 1 − 1 p k ) \varphi(n)=n\times(1-\dfrac{1}{p_1})\times(1-\dfrac{1}{p_2})\times···\times(1-\dfrac{1}{p_k}) φ(n)=n×(1−p11)×(1−p21)×⋅⋅⋅×(1−pk1)。
欧拉定理
若 a a a 与 m m m 互质,则 a φ ( m ) = 1 ( m o d m ) a^{\varphi(m)}=1\pmod m aφ(m)=1(modm)。
同余性质
前置芝士:费马小定理、欧拉定理
整数 a , b , c a,b,c a,b,c,自然数 m , n m,n m,n,模 m m m。
-
自反性: a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm)。
-
对称性:若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm),则 b ≡ a ( m o d m ) b\equiv a\pmod m b≡a(modm)。
-
传递性:若 a ≡ b ( m o d m ) , b ≡ c ( m o d m ) a\equiv b\pmod m,b\equiv c\pmod m a≡b(modm),b≡c(modm),则 a ≡ c ( m o d m ) a\equiv c\pmod m a≡c(modm)。
-
同加性:若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm),则 a + c ≡ b + c ( m o d m ) a+c\equiv b+c\pmod m a+c≡b+c(modm)。
-
同乘性:若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm),则 a c ≡ b c ( m o d m ) ac\equiv bc\pmod m ac≡bc(modm)。
一般情况,若 a ≡ b ( m o d m ) , c ≡ d ( m o d m ) a\equiv b\pmod m,c\equiv d\pmod m a≡b(modm),c≡d(modm),则 a c ≡ b d ( m o d m ) ac\equiv bd\pmod m ac≡bd(modm)。
-
同幂性:若 a ≡ b ( m o d m ) a\equiv b\pmod m a≡b(modm),则 a n ≡ b n ( m o d m ) a^n\equiv b^n\pmod m an≡bn(modm)。
-
若 a m o d p = x , a m o d q = x a\bmod p=x,a\bmod q=x amodp=x,amodq=x, p , q p,q p,q 互质,则 a m o d p q = x a\bmod pq=x amodpq=x。
乘法逆元
定义
若 a x = 1 ( m o d b ) ax=1 \pmod b ax=1(modb), a , b a,b a,b 互质,则称 x x x 为 a a a 的逆元,记为 a − 1 a^{-1} a−1。
作用
逆元可以在计算 t a m o d b \boxed{\dfrac{t}{a}\bmod b} atmodb 时,转化为 t × a − 1 m o d b \boxed{t\times a^{-1}\bmod b} t×a−1modb。
求法
扩展欧几里得算法
根据逆元的定义,可转化为 a x + b y = 1 ax+by=1 ax+by=1,用扩展欧几里得算法求解。时间复杂度 O ( log b ) O(\log b) O(logb)。
友情赠送代码:
void exgcd(int a,int b,int c,int &x,int &y)
{
if(a==0)
{
x=0;y=c/b;
return;
}
else
{
int tx,ty;
exgcd(b%a,a,tx,ty),x=ty-(b/a)*tx,y=tx;
return;
}
}
线性算法
前置芝士: 1 − 1 ≡ 1 ( m o d p ) 1^{-1}\equiv1\pmod p 1−1≡1(modp)。
设 p = k × i + r , r < i , 1 < i < p p=k\times i+r,r<i,1<i<p p=k×i+r,r<i,1<i<p,则: k × i + r ≡ 0 ( m o d p ) k\times i+r\equiv 0\pmod p k×i+r≡0(modp)。
两边同时乘 i − 1 , r − 1 i^{-1},r^{-1} i−1,r−1 就会得到:
k × r − 1 + i − 1 ≡ 0 i − 1 ≡ − k × r − 1 i − 1 ≡ − [ p i ] × ( p m o d i ) − 1 \begin{aligned} k\times r^{-1}+i^{-1}&\equiv0\\ i^{-1}&\equiv-k\times r^{-1}\\ i^{-1}&\equiv-\left[\dfrac{p}{i}\right]\times (p\bmod i)^{-1}\\ \end{aligned} k×r−1+i−1i−1i−1≡0≡−k×r−1≡−[ip]×(pmodi)−1
于是,就可以递归求逆元啦!代码只有一行!
inv[i]=-(p/i)*inv[p%i];
这种方法可以在 Θ ( log 2 p ) \Theta(\log_2p) Θ(log2p)(众所周知 Θ \Theta Θ 表示时间复杂度更准确 qwq)的时间内求出单个数逆元。
线性方程
前置芝士:人人都会的辗转相除法( ∀ a , b ∈ N , b ≠ 0 , gcd ( a , b ) = gcd ( b , a m o d b ) \forall a,b\in \mathbb{N},b\neq0,\gcd(a,b)=\gcd(b,a\bmod b) ∀a,b∈N,b=0,gcd(a,b)=gcd(b,amodb))
扩展欧几里得算法
定理 1
设 a ≠ 0 , b ≠ 0 a\neq0,b\neq0 a=0,b=0,存在整数 x 、 y x、y x、y,使得 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b)。
证明
在欧几里得算法的最后一步,当 b = 0 b=0 b=0 时, gcd ( a , b ) = a \gcd(a,b)=a gcd(a,b)=a。因为 1 × a + 0 × 0 = a 1\times a+0\times 0=a 1×a+0×0=a,所以 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 有一组解为 x = 1 , y = 0 x=1,y=0 x=1,y=0。
当 b ≠ 0 b\neq0 b=0 时,递归求 gcd ( b , a m o d b ) \gcd(b,a\bmod b) gcd(b,amodb),假设存在一组整数解 x ′ , y ′ x',y' x′,y′,满足 b x ′ + ( a m o d b ) y ′ = gcd ( b , a m o d b ) = gcd ( a , b ) bx'+(a\bmod b)y'=\gcd(b,a\bmod b)=\gcd(a,b) bx′+(amodb)y′=gcd(b,amodb)=gcd(a,b),那么可以推出:
b x ′ + ( a − ⌊ a b ⌋ × b ) y ′ = gcd ( a , b ) a y ′ + b ( x ′ − ⌊ a b ⌋ y ′ ) = gcd ( a , b ) \begin{aligned} &bx'+(a-\lfloor\dfrac{a}{b}\rfloor\times b)y'=\gcd(a,b)\\ &ay'+b(x'-\lfloor\dfrac{a}{b}\rfloor y')=\gcd(a,b) \end{aligned} bx′+(a−⌊ba⌋×b)y′=gcd(a,b)ay′+b(x′−⌊ba⌋y′)=gcd(a,b)
于是乎,令 x = y ′ , y = x ′ − ⌊ a b ⌋ y ′ x=y',y=x'-\lfloor\dfrac{a}{b}\rfloor y' x=y′,y=x′−⌊ba⌋y′。
代码如下:
(对于
&
取地址符的问题,如果不加,相当于把传进去的参量复制了一份进入到函数中,不会影响主函数里的那个值;如果加取地址符相当于直接把参量扔进去了)
void exgcd(int a,int b,int &g,int &x,int &y)//g是gcd(a,b)
{
if(!b) x=1,y=0,g=a;
else exgcd(b,a%b,g,y,x),y-=x
}
定理 2
对于不定方程 a x + b y = c ax+by=c ax+by=c,当且仅当 gcd ( a , b ) = c \gcd(a,b)=c gcd(a,b)=c 时,方程有整数解。
莫比乌斯反演
莫比乌斯函数
前置芝士:质数筛选
定义
设正整数 N N N 按照算数基本定理分解质因数为 p 1 c 1 p 2 c 2 ⋯ p m c m p_1^{c_1}p_2^{c^2}\cdots p_m^{c_m} p1c1p2c2⋯pmcm,定义函数
μ ( N ) = { 0 ∃ i ∈ [ 1 , m ] , c i > 1 1 m ≡ 0 ( m o d 2 ) , ∀ i ∈ [ 1 , m ] , c i = 1 − 1 m ≡ 1 ( m o d 2 ) , ∀ i ∈ [ 1 , m ] , c i = 1 \mu(N)= \begin{cases} 0&\exists i\in[1,m],c_i>1\\ 1&m\equiv0\pmod2,\forall i\in[1,m],c_i=1\\ -1&m\equiv1\pmod2,\forall i\in[1,m],c_i=1 \end{cases} μ(N)=⎩ ⎨ ⎧01−1∃i∈[1,m],ci>1m≡0(mod2),∀i∈[1,m],ci=1m≡1(mod2),∀i∈[1,m],ci=1
μ ( n ) \mu(n) μ(n) 即为莫比乌斯函数。
当 N N N 包含相等的质因子时, μ ( n ) = 0 \mu(n)=0 μ(n)=0。当 N N N 的所有质因子各不相等时,若 N N N 有偶数个质因子, μ ( N ) = 1 \mu(N)=1 μ(N)=1;若 N N N 有奇数个质因子, μ ( N ) = − 1 \mu(N)=-1 μ(N)=−1。
求法
若只求一项莫比乌斯函数,分解质因数即可。
若求 1 ∼ N 1\sim N 1∼N 的每一项莫比乌斯函数,可以使用埃氏筛。先把所以 μ \mu μ 初始化为 1 1 1。对于筛出的每一个质数 p p p,令 μ ( p ) = − 1 \mu(p)=-1 μ(p)=−1,扫描 p p p 的倍数,检查 x x x 能否被 p p p 整除。若能,则令 μ ( x ) = 0 \mu(x)=0 μ(x)=0;否则,令 μ ( x ) = − μ ( x ) \mu(x)=-\mu(x) μ(x)=−μ(x)。
代码如下:
-
埃氏筛
for(int i=1;i<=n;i++) mu[i]=1,v[i]=0; for(int i=2;i<=n;i++) { if(v[i]) continue; mu[i]=-1; for(int j=2*i;j<=n;j+=i) { v[j]=1; if((j/i)%i==0) mu[j]=0; else mu[j]*=-1; } }
-
线性筛
for(int i=2;i<=n;++i) { if(!vis[i]) p[++tot]=i,mu[i]=-1; for(int j=1;j<=tot&&1ll*p[j]*i<=n;++j) { int now=i*p[j]; vis[now]=1; if(i%p[j]==0) mu[now]=0; else {mu[now]=-mu[i];break} } }
性质
-
对任意正整数 n n n 有: ∑ d ∣ n μ ( d ) = { 1 n = 1 0 n > 1 \sum\limits_{d|n}\mu(d)=\begin{cases}1&n=1\\0&n>1\end{cases} d∣n∑μ(d)={10n=1n>1。
-
对任意正整数 n n n 有: ∑ d ∣ n μ ( d ) d = φ ( n ) n \sum\limits_{d|n}\dfrac{\mu(d)}{d}=\dfrac{\varphi(n)}{n} d∣n∑dμ(d)=nφ(n)。
莫比乌斯反演
莫比乌斯反演,可以简化运算。
定理:
F
(
n
)
F(n)
F(n) 和
f
(
n
)
f(n)
f(n) 是定义在非负整数集合中的两个函数,
F
(
n
)
=
∑
d
∣
n
f
(
d
)
F(n)=\sum\limits_{d|n}f(d)
F(n)=d∣n∑f(d),那么
f
(
n
)
=
∑
d
∣
n
μ
(
d
)
F
(
n
d
)
f(n)=\sum_{d|n}\mu(d)F(\dfrac{n}{d})
f(n)=d∣n∑μ(d)F(dn)
Lucas 定理
Lucas 定理主要用于解决大组合数取模的问题。注意这里的模数 p p p 不能太大,一般小于 1 0 5 10^5 105。
若
p
p
p 是质数,则对于任意整数
1
≤
m
≤
n
1\leq m\leq n
1≤m≤n,有:
C
n
m
=
C
n
m
o
d
p
m
m
o
d
p
×
C
⌊
n
/
p
⌋
⌊
m
/
p
⌋
(
m
o
d
p
)
C_n^m=C_{n\bmod p}^{m\bmod p}\times C_{\lfloor n/p\rfloor}^{\lfloor m/p\rfloor}\pmod p
Cnm=Cnmodpmmodp×C⌊n/p⌋⌊m/p⌋(modp)
相当于把
n
n
n 和
m
m
m 表示为
p
p
p 进制数,将
p
p
p 进制下的每一位数计算组合数相乘。