数论,数学
gcd and lcm
Code
int gcd(int a,int b){return b==0?a:gcd(b,a%b);}
int lcm(int a,int b){return a/gcd(a,b)*b;}
gcd的几条性质
证明:
gcd(xa−1,xb−1)
=gcd(xb−1,xa−xb)
=gcd(xb−1,xb(xa−b−1))
=gcd(xb−1,xa−b−1)
( fib[] 为广义斐波那契数列)
证明:
gcd(fib[i+j],fib[j])
=gcd(fib[j],fib[i+j]−fib[j])
=gcd(fib[j],fib[i−1]∗fib[j]+fib[i]∗fib[j+1]−fib[j])
=gcd(fib[j],fib[i]∗fib[j+1])
=gcd(fib[j],fib[i])
线性筛
int pr[maxn],tot;
bool check[maxn];
void init(int n){
check[1]=1;
for(int i=2;i<=n;i++){
if(!check[i]){
pr[++tot]=i;
}
for(int j=1;j<=tot&&i*pr[j]<=n;j++){
check[i*pr[j]]=1;
if(i%pr[j]==0)break;
}
}
}
理论上来讲 n 以内的质数个数大约在 n/ln(n) 个左右
欧拉定理
设 S={x1,x2,...,xφ(n)} 为所有与 n 互质且 ≤n 的数。
显然 xi≡xj(i≠j)(%n) 不成立。
再设 T={a∗x1,a∗x2,...,a∗xφ(n)}
\because \gcd(a,n)=1
\therefore a*x_i≡a*x_j(i≠j)(\% n) 不成立
\therefore S=T
\therefore \prod_{i=1}^{\varphi(n)}a*x_i≡\prod_{i=1}^{\varphi(n)}x_i(\% n)
\therefore a^{\varphi(n)}≡1(\% n)
扩展欧拉定理
费马小定理
a^{p-1}≡1(\% p)(p\text{ is prime})
Code
O(n) 求 1-n 的欧拉函数
void init(int n){
phi[1]=1;
for(int i=2;i<=n;i++){
if(!check[i]){
pr[++tot]=i;
phi[i]=i-1;
}
for(int j=1;j<=tot&&i*pr[j]<=n;j++){
check[i*pr[j]]=1;
if(i%pr[j]==0){
phi[i*pr[j]]=phi[i]*pr[j];
break;
}
else phi[i*pr[j]]=phi[i]*phi[pr[j]];
}
}
}
O(\sqrt{n}) 求 n 的欧拉函数
int phi(int n){
int x=n,ret=n;
for(int i=2;i*i<=n&&x>1;i++){
if(x%i==0){
ret=ret/i*(i-1);
while(x%i==0)x/=i;
}
}
if(x>1)ret=ret/x*(x-1);
return ret;
}
逆元
逆元的引入
在做组合数取模的时候,常常要求(a/b)\%p^{[1]},然而不同于加减乘,除法取模没有(a/b)\%p=(a\%p/b\%p)\%p的性质。因此,我们引入乘法逆元的概念。
若a* b≡1(\%p),则设b=inv(a),称b是a的逆元,满足(a*b)\%p=(a\%p*b\%p)\%p。这样,除法转换为乘法,就可以了。
所有的有理数都有一个与其对应的逆元。因此,对于两个整数A,B,要计算\frac{A}{B}(实数除法)时,A不一定要被B整除。
对于两个取模过的数,逆元是怎么输出商的?它实际上是在对给定的取模过的数a,b,找最小的两个A,B,满足A=x* p+a,B=y* p+b,A\%B=0,并返回A/B。
求逆元的几种常用方法
1. 扩展欧几里得
欧几里得定理
想必都知道gcd(a,b)=gcd(b,a%b)
解关于x,y的方程ax+by=gcd(a,b)
首先,我们先解出方程的一个特解,再通过特解推出其余的通解即可。
如何求特解
我们来看下面两个方程:
\because gcd(a,b)=gcd(b,a\%b),
\therefore ax_1+by_1=bx_2+(a\%b)y_2
\because a\%b=a-floor(a/b)* b^{[2]}
\therefore ax_1+by_1=ay_2+b(x_2-floor(a/b)* y_2)
由等式两边的系数关系得x_1=y_2y_1=x_2-floor(a/b)* y_2
于是形成了一种递归关系,可用递归解决。
递归终止状态
我们知道在计算最大公约数的过程中,b最终会等于0,此时a=gcd(A,B),因此,这时方程化为a* x=gcd(A,B),解得x=1,y=0。
如何求通解
求得一组特解之后,我们发现,对于方程ax+by=gcd(a,b),当x增加b/gcd,y减少a/gcd时,等式仍成立。
证明
ax项增加a* b/gcd,by项减少a* b/gcd。
于是可求得所有通解。
如何求最小非负整数解
此处以x为例,y同理。我们让x一直减B/gcd直至求出最小解,即让x\%(B/gcd)即可。
解关于x,y的方程Ax+By=C
我们发现,当C不能整除gcd(A,B)时,方程就不能化为ax+by=gcd(a,b)的形式,因此就无解。否则有无数解,将方程两边同时除以\frac{C}{gcd(A,B)}求解即可。
Code
typedef long long D;
D egcd(D a,D b,D &x,D &y){
if(b==0){
x=1;
y=0;
return a;
}
D ans=egcd(b,a%b,x,y),tmp=x;
x=y;
y=tmp-a/b*y;
return ans;
}
D cal(D a,D b,D c){
D x,y,g=egcd(a,b,x,y);
if(c%g!=0)return -1;
b=abs(b/g);
x=(x*(c/g)%b+b)%b;
return x;
}
利用扩展欧几里得求乘法逆元
因为a* x≡1(%p)
所以a* x+b* y=1
此时,若1\%gcd(a,b)≠1,即gcd(a,b)≠1,则该方程无解。
否则,设特解为x_0,最小非负整数解x=x_0\%m。当m<0时,由于计算机取模一个负数与数学上的意义不同,所以应把模数取绝对值。当x_0<0时,取模的结果是一个负数,所以把答案加上m即可。
复杂度为\log级。
Code
typedef long long D;
D egcd(D a,D b,D &x,D &y){
if(b==0){
x=1;
y=0;
return a;
}
D ans=egcd(b,a%b,x,y),tmp=x;
x=y;
y=tmp-a/b*y;
return ans;
}
D cal(D a,D m){
D x,y,g=egcd(a,m,x,y);
if(g!=1)return -1;
m=abs(m/g);
x=(x%m+m)%m;
return x;
}
2. 利用费马小定理
当p为质数时, a^{p-1}≡1(\%p) ,对a* x≡1(\%p)有x=a^{p-2}\%p。
复杂度为\log级。
Code
typedef long long D;
D qpow(D x,D y,D p){
D ans=1;
while(y){
if(y&1)ans=ans*x%p;
x=x*x%p;
y>>=1;
}
return ans;
}
D cal(D a,D p){
return qpow(a,p-2,p);
}
组合数取模(Lucas 定理)
由于题目中给的p一般都是质数,所以下面我们默认p为质数。
1. 错误方法
由上面的结论,我们得出C(n,m)=\frac{n!}{m!(n-m)!}=n!* inv(m!)* inv((n-m)!)
直接使用扩展欧几里得或费马小定理求逆元来实现除法取模。
此方法的错误之处在于,对a* x≡1(\%p),a有逆元的前提条件时a,p互质。当a,p不互质,即a是p的倍数时,不存在逆元,也就无法计算。
比如计算C(7,5)\%5,正确答案应该是\frac{7* 6}{1* 2}\%5=21\%5=1,但计算过程中出现了5的倍数,取模5之后就变为0,因此最后答案输出了0。
2. Lucas定理
lucas定理可避免上述情况。
递归式:
设lucas(n,m,p)为C(n,m)\%p (p为质数),则
此处的C(n,m,p)可直接通过逆元计算。
复杂度为\log级。
证明
Code
typedef long long D;
D fac[100001];
void preparefac(D n,D p){
fac[0]=1;
for(D i=1;i<=n;i++){
fac[i]=i*fac[i-1]%p;
}
}
D qpow(D x,D y,D p){
D ans=1;
while(y>0){
if(y%2)ans=ans*x%p;
x=x*x%p;
y/=2;
}
return ans;
}
D cal(D x,D y){
return qpow(x,y-2,y);
}
D Div(D x,D y,D p){
return x*cal(y,p)%p;
}
D C_(D n,D m,D p){
if(m>n)return 0;
return Div(Div(fac[n],fac[m],p),fac[n-m],p);
}
D C(D n,D m,D p){
if(!m)return 1;
return C_(n%p,m%p,p)*C(n/p,m/p,p)%p;
}
线性递推求逆元
首先 1^{-1}≡1(\%p)
设 p=k* i+r
则 k* i+r≡0(\%p)
等式两边乘 i^{-1}* r^{-1}
得 k* r^{-1}+i^{-1}≡0(\%p)
\therefore i^{-1}≡-k* r^{-1}(\%p)
\therefore i^{-1}≡-floor(\frac{p}{i})* (p\%i)^{-1}(\%p)
Code
inv[1]=1;
inv[i]=(p-p/i)*inv[p%i]%p;
阶乘逆元
i 的阶乘逆元等于 i-1 的阶乘逆元乘以 i 的逆元。
Code
facinv[i]=facinv[i-1]*inv[i]%p;
CRT
用于求解一元线性同余方程组 x\equiv a_i\pmod{m_i}。
过程:
- b_i=\prod_{j\neq i}m_j
- c_i\equiv b_i^{-1}\pmod{m_i}
- ans=\sum_{i=1}^n a_ib_ic_i\pmod{\prod_i m_i}
证明
对于i\in[1,n],ans\equiv a_i+\sum_{j\neq i} a_jb_jc_j\pmod{m_i},由于m_i|b_j(j\neq i),\therefore ans\equiv a_i\pmod{m_i}
exCRT
对于多个方程的情况,考虑将方程两两合并。
对于两个方程的情况x\equiv a_1\pmod{m_1},x\equiv a_2\pmod{m_2}
转换:x=m_1s+a_1=m_2t+a_2 \Rightarrow m_1s-m_2t=a_2-a_1,使用exgcd求解
当\gcd(m_1,m_2)\nmid a_2-a_1时无解
高端数论
积性函数
f(xy)=f(x)f(y)(x\perp y)
完全积性函数
f(xy)=f(x)f(y)
欧拉函数
定义
\varphi(n)=\sum_{i=1}^n [i\perp n]
性质
显然,除了它本身以外的数都和该质数互质。
与 p^k 不互质的数就是 p 的倍数,有 \frac{p^k}{p}=p^{k-1} 个。
设 S_x=\{ 所有与 x 互质且 \le x 的数 \} 。显然 |S_x|=\varphi(x) 。
\because \gcd(a,b)=1,
\therefore S_{a*b} 与 S_a,S_b 一一对应。
\therefore |S_{a*b}|=|S_a|*|S_b|, 即 \varphi(a*b)=\varphi(a)*\varphi(b)
\varphi(2)=1 ,易证。
\varphi(\frac{n}{p})=\frac{n}{p}*\prod (1-\frac{1}{p_i})
\varphi(n)=p*\frac{n}{p}*\prod (1-\frac{1}{p_i})=p*\varphi(\frac{n}{p})
\varphi(p)=p-1 ,易证。
设 f(n)=\sum_{d|n}\varphi(d)
当 \gcd(x,y)=1 时, f(x)*f(y)=x\prod_{p_i是x的质因子}(1-\frac{1}{p_i})*y\prod_{p_i是y的质因子}(1-\frac{1}{p_i})=x*y\prod_{p_i是x*y的质因子}(1-\frac{1}{p_i})=f(x*y)
\therefore f(n) 是积性函数。
f(p^k)=\sum_{i=0}^k \varphi(p^i)=p^k
于是我们把 n 分解质因数:
n=\prod p_i^{k_i}
f(n)=\prod f(p_i^{k_i})=\prod p_i^{k_i}=n
公式
\varphi(x)=x\prod (1-\frac{1}{p_i})
p_i 为 x 的第 i 个质因子, x≥2 。 \varphi(1)=1 。
根据唯一分解定理, x=\prod p_i^{k_i} 。
\therefore \varphi(x)=\prod \varphi(p_i^{k_i})=\prod (p_i^{k_i}-p_i^{k_i-1})=x\prod (1-\frac{1}{p_i})
其他常用函数
\epsilon(n)=[n=1]
id(n)=n
1(n)=1
\sigma_k(n)=\sum_{d\mid n}d^k
其中\omega(n)表示n的本质不同质因子个数,是一个积性函数。
狄利克雷卷积
(f*g)(n)=\sum_{d\mid n}f(d)g(\frac{n}{d})
例子
\varphi=\mu*id证明
\varphi*1=id \Rightarrow \varphi*1*\mu=id*\mu \Rightarrow \varphi*\epsilon=\mu*id
莫比乌斯反演
f(n)=\sum_{d\mid n}g(d)\Rightarrow g(n)=\sum_{d\mid n}\mu(d)f(\frac{n}{d})
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· .NET 9 new features-C#13新的锁类型和语义
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 《HelloGitHub》第 106 期
· 数据库服务器 SQL Server 版本升级公告
· 深入理解Mybatis分库分表执行原理
· 使用 Dify + LLM 构建精确任务处理应用