因为本人数论较差,于是在noip前复习发现较大漏洞,特此作此篇记录一下。
一、逆元相关
首先我们先引入%运算
(a + b) % p = (a%p + b%p) %p (对)
(a - b) % p = (a%p - b%p) %p (对)
(a * b) % p = (a%p * b%p) %p (对)
(a / b) % p = (a%p / b%p) %p (错)
很容易就发现四则运算中,只有除法是不符合的,那么如果我们在做题时遇到了除法又要取余的时候,应该怎么办呢?
下面我们就引入逆元这个概念a*x ≡ 1 (mod p) (a和p互质)
那么x就是a在模p意义下的逆元,一般用inv(a)表示
那么前面这个式子就可以这么表示了(a / b) % p = (a * inv(a) ) % p = (a % p * inv(a) % p) % p
下面来讲几个求逆元的方法
1、费马相关
根据逆元的定义 a*x ≡ 1 (mod p)
以及费马小定理 a^(p-1) ≡ 1 (mod p)
那么我们两边同除a,就可以得到 a^(p-2) ≡ inv(a) (mod p)
而这就是a的逆元,时间复杂度O(log p)
2、递推求1-n的逆元
设p=a*i+b,即a=p/i,b=p%i,那么 a*i+b ≡ 0 (mod p)
两边同时乘上 (i-1)*(b-1)
就会得到 a*(b-1)+(i-1) ≡ 0 (mod p)
移项得(i-1) ≡ -a*(b^-1) (mod p)
代入前面设的值 (i-1) ≡ -(p/i)*inv(p%i) (mod p)
即inv[i] = -(p/i) * inv[p%i]
二、扩展欧几里得
对于一个方程 ax + by = 1 当且仅当 a和b互质时才有解
而这个的原型就是 ax+by=gcd(a,b) 而他的通解的解法就是扩欧
因为gcd的辗转相除法 gcd(a,b)=gcd(b,a%b)
所以以上那个方程通过变化可以变成 bx' + (a%b)y' = gcd(a,b)
那么 ax+by = bx' + (a%b)y'
通过移项可变成 ax+by = ay'+b(x'-(a)y')
现在就可以很明显的得到一个递归式子 x=y',y=x'-(a)y'
而求这个的方法则类似gcd的做法
void exgcd(LL a, LL b, LL &x, LL &y, LL &d){ if (b==0) {d=a; x=1;y=0; return;} exgcd(b,a%b,y,x,d); y-=x*(a/b); }
我们再回来看看求逆元的第三种方法
3、扩欧相关
根据逆元的定义 a*x ≡ 1 (mod p)
把它看成 a*x + p*y ≡ 1 (mod p)
显然当且仅当 gcd(a, p) = 1 时存在一组解(x, y)满足条件,也就是存在a mod p的逆元
证明如下:a*x % b + b*y % b = 1 % b
a*x % b = 1 % b 转化为 a*x ≡ 1 (mod b)
void exgcd(LL a, LL b, LL &x, LL &y, LL &d){ if (b==0) {d=a; x=1;y=0; return;} exgcd(b,a%b,y,x,d); y-=x*(a/b); } LL inv(LL t, LL p){ LL d,x,y; exgcd(t,p,x,y,d); return d==1?(x%p+p)%p:-1; }
4、求阶乘的逆元
因为 (a !) * x ≡ 1 (mod p)
那么 (a-1) ! * a%p *x ≡ 1 (mod p)
那么 (a-1) ! 的逆元就是 (a%p*x) ,就是a的阶乘的逆元 *a%p
那么我们就可以先求出 n! 的逆元,然后就可以O(1) 逆推求其他阶乘的逆元了