简单数学
突然发现之前也写过一篇文章叫“简单数学”,缘,妙不可言
博主整理了一下\(OI\)中简单的数论知识,故取名简单数学,以后会随着学习的深入慢慢扩展开来。
以下的代码部分没经过验证,可能一定会出锅
质数
质数的判定
顾名思义就是判定某一个数是不是质数
试除法
一个结论:
若一个正整数\(N\)为合数,那么一定存在一个正整数\(T\)能够整除\(N\),且\(2\le N \le \sqrt N\)
该结论可以通过反证法证明,这里不再赘述(直接背过他不香吗?)
bool judge_prime(int N){
for(int i=2;i*i<=N;++i)
if(N%i==0) return 0;
return 1;
}
容易看出复杂度为\(O( \sqrt N)\)
质数的筛选
顾名思义就是晒出出\(1\backsim N\)中的质数
Eratosthenes筛法
核心思想就是质数的倍数是合数,具体不再赘述
void primes(int n){
memset(v,0,sizeof(v));
for(int i=2;i<=n;++i)
{
if(v[i]) continue;
prime[++cnt]=i;//i是质数
for(int j=i*2;j<=n;j+=i) v[j]=1;
}
}
复杂度为\(O(\sum_{质数p\le \frac{N}{p}})=O(N\ \log \log N)\)
线性筛法
核心思想就是通过“从小到大积累质因子”的方式来标记合数,以此来避免\(Eratosthenes\)筛法重复标记合数的问题
具体实现原理简单来说就是:每个合数\(i*p\)只会被它的最小质因子筛一次
void primes(int n){
memset(v,0,sizeof(v));//最小质因子
cnt=0;//质数的数量
for(int i=2;i<=n;++i)
{
if(v[i]==0) {prime[++cnt]=i;v[i]=i;}
for(int j=1;j<=cnt;++j)
{
if(prime[j]>v[i]||prime[j]>n/i) break;
v[i*prime[j]]=prime[j];
}
}
}
//于2020.7.6更正代码
复杂度为\(O(N)\)我一般都是用埃氏筛
算术基本定理
任何一个大于\(1\)的正整数都能被唯一分解为有限个质数的乘积的形式,写作:
其中\(c_i\in\N^+\),\(p_i\)都是质数,且满足\(p_1<p_2<...<p_m\)
试除法分解质因数
就是结合一下埃氏筛和试除法
void divide(int n)
{
cnt=0;
for(int i=2;i*i<=n;++i)
{
if(n%i==0)
{
p[++cnt]=i;c[cnt]=0;
while(n%i==0) n/=i,c[cnt]++;
}
}
if(n>1) p[++cnt]=n,c[cnt]=1;
}
复杂度为\(O( \sqrt N )\)
约数
算术基本定理的推论
如果一个正整数被分解成了\(N=p_1^{c_1}p_2^{c_2}...p_m^{c_m}\)这种形式,则\(N\)的正约数集合可以写作
\(p_1^{b_1}p_2^{b_2}...p_m^{b_m}\)其中\(0\le b_i \le c_i\)
约数个数
\(N\)的正约数个数为\(\prod\limits_{i=1}^m(c_i+1)\)
约数和
\(N\)的所有正约数和为\(\prod\limits_{i=1}^m(\sum\limits_{j=0}^{c_i}(p_i)^j)\)
求\(N\)的正约数集合
试除法
核心就是利用约数总是成对出现(除了对于完全平方数,\(\sqrt{N}\)会单独出现)
for(int i=1;i*i<=N;++i)
{
if(n%i==0)
{
d[++cnt]=i;
if(i!=n/i) d[++cnt]=n/i;
}
}
复杂度为\(O( \sqrt N)\)
试除法的推论
一个正整数的约数个数上界为\(2 \sqrt N\)
倍数法
求\(1\backsim N\)中每个数的正约数集合
核心思路就是对于每个约数\(d\),以它为约数的正整数为\(d,2d,3d...\left\lfloor\dfrac{N}{d}\right\rfloor*d\)
for(int i=1;i<=n;++i)
for(int j=1;j<=n/i;++j)
f[i*j].push_back(i);
复杂度为\(O(N+\frac{N}{2}+\frac{N}{3}...+\frac{N}{N})=O(N\log N)\)
倍数法推论
\(1\backsim N\)中每个数的约数个数的总和大约为\(N \log N\)
最大公约数
定理
\(\forall a,b\in \N \ \ \ \gcd(a,b)*lcm(a,b)=a*b\)
更相减损法
\(\forall a,b\in \N,a\ge b \ \ \ \ \gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)\)
\(\forall a,b\in \N \ \ \ \gcd(2a,2b)=\gcd(a,b)\)
如果需要高精度计算时,高精度除法(取模)很恶心人,可以用更相减损法来替代欧几里得算法
欧几里得法
\(\forall a,b\in \N \ \ \ \gcd(a,b)=\gcd(b,a\%b)\)
int gcd(int a,int b)
{
if(!b) return a;
return gcd(b,a%b)
}
互质与欧拉函数
欧拉函数
定义
\(1\backsim N\)中与\(N\)互质的数的个数,记为\(\varphi(N)\)
$\varphi(N)=N*\prod\limits_{质数p|N}(1-\frac{1}{p}) $
于2020.11.6修改错误/kk
积性函数
如果\(a,b\)互质时,有\(f(ab)=f(a)*f(b)\),那么函数\(f\)就是积性函数
积性函数的性质
若\(f\)是积性函数,且在算术基本定理中\(n=\prod_{i=1}^mp_i^{c_i}\)则\(f(n)=\prod_{i=1}^{m}f(p_i^{c_i})\)
欧拉函数基本性质
- \(\forall\ n>1,1\backsim n\)中与\(n\)互质的数的和为\(n*\varphi(n)/2\)
- 若\(a,b\)互质,则\(\varphi(ab)=\varphi(a)*\varphi(b)\)(积性函数的性质)
- 设\(p\)是质数,若\(p\mid n\)且\(p^2\mid n\),则\(\varphi(n)=\varphi(n/p)*p\)
- 设\(p\)是质数,若\(p\mid n\)但是\(p^2 \nmid n\)则\(\varphi(n)=\varphi(n/p)*(p-1)\)
- \(\sum_{d\mid n}\varphi(d)=n\)
试除法求欧拉函数
根据欧拉函数的计算计算式,可以在分解质因数时顺便求出欧拉函数
int phi(int n)
{
int ans=n;
for(int i=2;i<sqrt(n);++i)
if(n%i==0)
{
ans=ans/i*(i-1);
while(n%i==0) n/=i;
}
if(n>1) ans=ans/n*(n-1);
return ans;
}
复杂度为\(O(\sqrt N)\)
\(Eratosthenes\)筛法求欧拉函数
根据欧拉函数的计算式,可以在\(O(n \log n)\)的时间内求出\(2 \backsim n\)中每个数的欧拉函数
void euler(int n)
{
for(int i=2;i<=n;++i) phi[i]=i;
for(int i=2;i<=n;++i)
if(phi[i]==i)
for(int j=i;j<=n;j+=i)
phi[j]=phi[j]/i*(i-1);
}
线性筛快速递推欧拉函数
- 设\(p\)是质数,若\(p\mid n\)且\(p^2\mid n\),则\(\varphi(n)=\varphi(n/p)*p\)
- 设\(p\)是质数,若\(p\mid n\)但是\(p^2 \nmid n\)则\(\varphi(n)=\varphi(n/p)*(p-1)\)
利用上述性质,在线性筛每个合数\(n\)被最小质因子\(p\)筛掉的时候通过\(\varphi(n/p)\)递推到\(\varphi(n)\)
代码就不写了,我没写过这种方法(我菜)
同余
同余,同余类和剩余系的定义
自己百度吧,懒得写了\(qwq\)
费马小定理
若\(p\)为质数,则对于任意整数\(a\),都有\(a^p \equiv a (\mod p)\)
欧拉定理
若正整数\(a,n\)互质,则\(a^{\varphi(n)} \equiv 1(\mod n)\)
欧拉定理的推论
若正整数\(a,n\)互质,则\(a^n \equiv a^{n\% \varphi(n)}(\mod n)\)
若正整数\(a,n\)不一定互质时,且\(b> \varphi (n)\),\(a^b \equiv a^{b\%\varphi(n)+\varphi(n)}(\mod n)\)
可用于欧拉降幂
裴蜀定理
对于任意整数\(a,b\),都存在一对整数\(x,y\),满足\(ax+by=\gcd(a,b)\)
扩展欧几里得算法
int exgcd(int a,int b,int &x,int &y)
{
if(b==0) {x=1;y=0;return a;}
int d=exgcd(b,a%b,x,y);
int z=x;x=y;y=z-y*(a/b)
return d;
}
注意:
上述程序求出方程\(ax+by=\gcd(a,b)\)的一组特解\(x_0,y_0\)
对于更为一般的方程\(ax+by=c\)而言,当\(d\mid c\)时方程有解,此时方程的解为\((c/d)*x_0,(c/d)*y_0\)
方程\(ax+by=c\)的通解可以表示为:
\(x=\frac{c}{d}x_0+k\frac{b}{d}\)
\(y=\frac{c}{d}y_0-k\frac{a}{d}\)
通过上式就可以求出最小正整数解了,\(qwq\)
乘法逆元的计算和应用
大概就是一个整数,能让\(a/b\)在模\(p\)的意义下等于\(a*inv(b)\)
然后就可以到处取模了,\(qwq\)
逆元的计算
根据费马小定理求解
对于\(a/b(\mod p)\)而言
若\(p\)为质数,则根据费马小定理,\(b^{p-1} \equiv 1(\mod p)\)
即\(b*b^{p-2}\equiv 1(\mod p)\)
\(b^{p-2}\)即为\(b\)的乘法逆元
求解同余方程
如果只保证\(b,p\)互质,则可以通过直接求解同余方程\(b*x\equiv 1(\mod p)\)
线性递推逆元
利用阶乘递推逆元
由于\(a=\frac{a!}{(a-1)!}\)
\(a^{-1}\equiv(a-1)!a!^{-1}\)
\((a-1)!\equiv a!^{-1}a\)
可以求出\(1...k\)的阶乘,然后利用快速幂求出\(k!\)的逆元,然后反推\(1...k\)的阶乘的逆元
该方法也可以求出任意\(k\)个数的阶乘,即把阶乘换成前缀和