质数筛法
Eratosthenes筛选法
顾名思义就是数学家 Eratosthenes 发明的筛法,简称为埃氏筛。
基本思想
质数的倍数一定不是质数。
实现方法
用一个长度为 N+1N+1N+1 的数组保存信息,000 表示质数,111 表示合数。先假设所有的数都是质数(初始化为 000),从小到大枚举每一个质数 xxx,把 xxx 的倍数都标记为非质数(置为 111)。
如何枚举质数 xxx 呢?从小到大扫描到 xxx 时,若 xxx 未被标记,则它不能被 2∼x−12\sim x-12∼x−1 之间的任何数整除,则 xxx 为质数。
举个例子:
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,⋯⇓⋯
可以发现,存在重复标记的耗时行为。实际上,小于 x2x^2x2 的 xxx 的倍数在扫描更小的数时就已经被标记过了。因此,可以优化一下,对于每个 xxx,把大于等于 x2x^2x2 的 xxx 的倍数标记为合数即可。
代码如下:
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(nloglogn)O(n\log\log n)O(nloglogn),效率非常接近于线性。时间复杂度的证明非常复杂,本蒟蒻也不太会,所以不证了
线性筛法
即使在优化后,埃氏筛仍然会重复标记合数。举个栗子,121212 既会被 222 标记又会被 333 标记,其根本原因是算法不能确定唯一的产生 121212 的方式。
所以,我们只要保证合数被它的最小质因数筛去就好啦!时间复杂度为 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;
}
费马小定理、欧拉定理
费马小定理
如果 ppp 是一个质数,而整数 aaa 不是 ppp 的倍数,则有 ap−1=1(modp)a^{p-1}=1\pmod pap−1=1(modp)。
一般情况:ap=a(modp)a^p=a \pmod pap=a(modp)
欧拉定理
欧拉函数
对正整数 nnn,欧拉函数是小于等于 nnn 的数中与 nnn 互质的数的数目。
引理 1
- 如果 nnn 为某一个素数 ppp,则:φ(p)=p−1\varphi(p)=p-1φ(p)=p−1;
- 如果 nnn 为某一个素数的 ppp 的幂次 pnp^npn,则:φ(pn)=(p−1)×pn−1\varphi(p^n)=(p-1) \times p^{n-1}φ(pn)=(p−1)×pn−1;
- 如果 nnn 为任意两个互质的数 a,ba,ba,b 的积,则:φ(a×b)=φ(a)φ(b)\varphi(a\times b)=\varphi(a)\varphi(b)φ(a×b)=φ(a)φ(b)。
引理 2
设 n=p1a1×p2a2×⋅⋅⋅×pkakn=p_1^{a_1}\times p_2^{a_2}\times···\times p_k^{a_k}n=p1a1×p2a2×⋅⋅⋅×pkak 为正整数 nnn 的素数幂乘积表达式,则:φ(n)=n×(1−1p1)×(1−1p2)×⋅⋅⋅×(1−1pk)\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)。
欧拉定理
若 aaa 与 mmm 互质,则 aφ(m)=1(modm)a^{\varphi(m)}=1\pmod maφ(m)=1(modm)。
同余性质
前置芝士:费马小定理、欧拉定理
整数 a,b,ca,b,ca,b,c,自然数 m,nm,nm,n,模 mmm。
-
自反性:a≡b(modm)a\equiv b\pmod ma≡b(modm)。
-
对称性:若 a≡b(modm)a\equiv b\pmod ma≡b(modm),则 b≡a(modm)b\equiv a\pmod mb≡a(modm)。
-
传递性:若 a≡b(modm),b≡c(modm)a\equiv b\pmod m,b\equiv c\pmod ma≡b(modm),b≡c(modm),则 a≡c(modm)a\equiv c\pmod ma≡c(modm)。
-
同加性:若 a≡b(modm)a\equiv b\pmod ma≡b(modm),则 a+c≡b+c(modm)a+c\equiv b+c\pmod ma+c≡b+c(modm)。
-
同乘性:若 a≡b(modm)a\equiv b\pmod ma≡b(modm),则 ac≡bc(modm)ac\equiv bc\pmod mac≡bc(modm)。
一般情况,若 a≡b(modm),c≡d(modm)a\equiv b\pmod m,c\equiv d\pmod ma≡b(modm),c≡d(modm),则 ac≡bd(modm)ac\equiv bd\pmod mac≡bd(modm)。
-
同幂性:若 a≡b(modm)a\equiv b\pmod ma≡b(modm),则 an≡bn(modm)a^n\equiv b^n\pmod man≡bn(modm)。
-
若 a mod p=x,a mod q=xa\bmod p=x,a\bmod q=xamodp=x,amodq=x,p,qp,qp,q 互质,则 a mod pq=xa\bmod pq=xamodpq=x。
乘法逆元
定义
若 ax=1(modb)ax=1 \pmod bax=1(modb),a,ba,ba,b 互质,则称 xxx 为 aaa 的逆元,记为 a−1a^{-1}a−1。
作用
逆元可以在计算 ta mod b\boxed{\dfrac{t}{a}\bmod b}atmodb 时,转化为 t×a−1 mod b\boxed{t\times a^{-1}\bmod b}t×a−1modb。
求法
扩展欧几里得算法
根据逆元的定义,可转化为 ax+by=1ax+by=1ax+by=1,用扩展欧几里得算法求解。时间复杂度 O(logb)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(modp)1^{-1}\equiv1\pmod p1−1≡1(modp)。
设 p=k×i+r,r<i,1<i<pp=k\times i+r,r<i,1<i<pp=k×i+r,r<i,1<i<p,则:k×i+r≡0(modp)k\times i+r\equiv 0\pmod pk×i+r≡0(modp)。
两边同时乘 i−1,r−1i^{-1},r^{-1}i−1,r−1 就会得到:
k×r−1+i−1≡0i−1≡−k×r−1i−1≡−[pi]×(p mod 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];
这种方法可以在 Θ(log2p)\Theta(\log_2p)Θ(log2p)(众所周知 Θ\ThetaΘ 表示时间复杂度更准确 qwq)的时间内求出单个数逆元。
线性方程
前置芝士:人人都会的辗转相除法(∀a,b∈N,b≠0,gcd(a,b)=gcd(b,a mod 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≠0a\neq0,b\neq0a=0,b=0,存在整数x、yx、yx、y,使得 ax+by=gcd(a,b)ax+by=\gcd(a,b)ax+by=gcd(a,b)。
证明
在欧几里得算法的最后一步,当 b=0b=0b=0 时,gcd(a,b)=a\gcd(a,b)=agcd(a,b)=a。因为 1×a+0×0=a1\times a+0\times 0=a1×a+0×0=a,所以 ax+by=gcd(a,b)ax+by=\gcd(a,b)ax+by=gcd(a,b) 有一组解为x=1,y=0x=1,y=0x=1,y=0。
当 b≠0b\neq0b=0 时,递归求 gcd(b,a mod b)\gcd(b,a\bmod b)gcd(b,amodb),假设存在一组整数解 x′,y′x',y'x′,y′,满足 bx′+(a mod b)y′=gcd(b,a mod 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),那么可以推出:
bx′+(a−⌊ab⌋×b)y′=gcd(a,b)ay′+b(x′−⌊ab⌋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′−⌊ab⌋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
对于不定方程 ax+by=cax+by=cax+by=c,当且仅当 gcd(a,b)=c\gcd(a,b)=cgcd(a,b)=c 时,方程有整数解。
莫比乌斯反演
莫比乌斯函数
前置芝士:质数筛选
定义
设正整数 NNN 按照算数基本定理分解质因数为 p1c1p2c2⋯pmcmp_1^{c_1}p_2^{c^2}\cdots p_m^{c_m}p1c1p2c2⋯pmcm,定义函数
μ(N)={0∃i∈[1,m],ci>11m≡0(mod2),∀i∈[1,m],ci=1−1m≡1(mod2),∀i∈[1,m],ci=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) 即为莫比乌斯函数。
当 NNN 包含相等的质因子时,μ(n)=0\mu(n)=0μ(n)=0。当 NNN 的所有质因子各不相等时,若 NNN 有偶数个质因子,μ(N)=1\mu(N)=1μ(N)=1;若 NNN 有奇数个质因子,μ(N)=−1\mu(N)=-1μ(N)=−1。
求法
若只求一项莫比乌斯函数,分解质因数即可。
若求 1∼N1\sim N1∼N 的每一项莫比乌斯函数,可以使用埃氏筛。先把所以 μ\muμ 初始化为 111。对于筛出的每一个质数 ppp,令 μ(p)=−1\mu(p)=-1μ(p)=−1,扫描 ppp 的倍数,检查 xxx 能否被 ppp 整除。若能,则令 μ(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} } }
性质
-
对任意正整数 nnn 有:∑d∣nμ(d)={1n=10n>1\sum\limits_{d|n}\mu(d)=\begin{cases}1&n=1\\0&n>1\end{cases}d∣n∑μ(d)={10n=1n>1。
-
对任意正整数 nnn 有:∑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∣nf(d)F(n)=\sum\limits_{d|n}f(d)F(n)=d∣n∑f(d),那么
f(n)=∑d∣nμ(d)F(nd)
f(n)=\sum_{d|n}\mu(d)F(\dfrac{n}{d})
f(n)=d∣n∑μ(d)F(dn)
Lucas 定理
Lucas 定理主要用于解决大组合数取模的问题。注意这里的模数 ppp 不能太大,一般小于 10510^5105。
若 ppp 是质数,则对于任意整数 1≤m≤n1\leq m\leq n1≤m≤n,有:
Cnm=Cn mod pm mod p×C⌊n/p⌋⌊m/p⌋(modp)
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)
相当于把 nnn 和 mmm 表示为 ppp 进制数,将 ppp 进制下的每一位数计算组合数相乘。