取模(mod)
运算符:%
a%b:a÷b的余数
对于取模,有以下性质:
- (a+b)modp=((amodp)+(bmodp))modp
- (a−b)modp=((amodp)−(bmodp))modp
- (a×b)modp=((amodp)×(bmodp))modp
但对于相除再取模,就得另想办法,这就说到了我们的逆元
逆元
注:逆元指的一般是乘法逆元,至于其它的逆元我也不太了解
如果有一个数 b′ ,满足b×b′modp=1,我们则称b′是b的乘法逆元
特别的,只有在gcd(b,p)=1(即b与p互质)的前提下才存在乘法逆元
有了逆元,我们就可以将相除再取模转换为相乘再取模
a÷bmodp=a÷b×1modp=a÷b×(b×b′)modp=a÷b×b×b′modp=a×b′modp
(b′是b的乘法逆元)
那么逆元怎么找呢,往下看
费马小定理
当p为质数时,对于任意的b,满足bp−1modp=1
我们可以拆解成b×bp−2modp=1
所以b的乘法逆元为bp−2
但如果p不为质数,则需要使用欧拉定理
欧拉定理
bφ(p)modp=1
φ(p)又可以写作phi(p),欧拉函数,表示的是1~p中与p互质的数的个数
但是,我们发现,如果暴力求φ(p)那么会复杂度会非常之高
那么这里提供一个新的φ(p)的求法
我们可以把p做一个质因数分解,得到:p=p1×p2×...×pm
那么φ(p)=p×p1p1−1×p2p2−1×...×pmpm−1
逆元打表
如果我们想要一次性求一个数列的逆元,我们可以使用递推法
对于一个质数p,求从1~n中(modp)的乘法逆元,我们有以下递推公式:
{inv1=1invi=(p−p÷i)×invpmodimodp
注:invi表示i的乘法逆元(modp)
拓展欧几里德找逆元
要求a的逆元(modp),我们可以求到一组x,y使得a×x+p×y=gcd(a,p),则(xmodp+p)modp
证明:
由于逆元存在的条件为gcd(a,p)=1,所以我们可以直接将a×x+p×y=gcd(a,p)写成a×x+p×y=1
随后,我们在等式两边同时模一个p,因为1模任何正整数均为1,因此可以的出(a×x+p×y)modp=1
根据取模的分配率可以及将其写成a×xmodp+p×ymodp=1
而因为p×y一定是p的倍数,所以p×ymodp=0
因此,我们可以将a×xmodp+p×ymodp=1写成a×xmodp=1
所以x则为a的乘法逆元
不过,此时x并不一定就是正数,所以我们需要通过(xmodp+p)modp将其转换为非负数
所以,得证
证明出这个之后,我们就可以通过拓展欧几里德来找逆元,比欧拉定理还快。由于我拓展欧几里德不是特别熟悉,这里放个代码吧
int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int d = exgcd(b,a%b,y,x);
y -= a/b*x;
return d;
}
int inv(int a,int p){
int x,y;
exgcd(a,p,x,y);
return (x%p+p)%p;
}