Loading docs/math/inverse.md +95 −0 Original line number Diff line number Diff line [【模板】乘法逆元](https://www.luogu.org/problemnew/show/P3811) ## 逆元简介 如果一个线性同余方程 $ax \equiv 1 \pmod b$,则 $x$ 称为 $a$ 的逆元,记作 $a^{-1}$。 ## 如何求逆元 ### 扩展欧几里得法: ```cpp void exgcd(int a,int b,int c,int &x,int &y){ if(a==0){ x=0; y=0; return; } else{ int tx,ty; exgcd(b%a,a,tx,ty); x=ty-(b/a)*tx; y=tx; return; } } ``` 扩展欧几里得法和求解线性同余方程是一个原理,在这里不做解释。 ### 快速幂法: 这个要运用费马小定理: > 若 $p$ 为质数,$a$ 为正整数,且 $a$、$p$ 互质,则 $a^{p-1} \equiv 1 \pmod p$。 关于费马小定理在[这里]() 因为 $ax \equiv 1 \pmod b$; 所以 $ax \equiv a^{b-1} \pmod b$(根据费马小定理); 所以 $x \equiv a^{b-2} \pmod b$; 然后我们就可以用快速幂来求了。 代码: ```cpp #define ll long long inline ll poW(ll a,ll b){ long long ans=1; a%=p; while (b){ if (b&1) ans=((ans*a)%p+p)%p; a=(a*a)%p; b>>=1; } return ans%p; } ``` ### 线性求逆元 但是如果要求的很多,以上两种方法就显得慢了,很有可能超时,所以下面来讲一下如何线性求逆元。 首先,很显然的 $1^{-1} \equiv 1 \pmod p$; 然后,设 $p=ki+j,j<i,1<i<p$,再放到 $\mod p$ 意义下就会得到:$ki+j \equiv 0 \pmod p$; 两边同时乘 $i^{-1},j^{-1}$: $kj^{-1}+i^{-1} \equiv 0 \pmod p$; $i^{-1} \equiv -kj^{-1}+ \pmod p$; $i^{-1} \equiv -(\frac{p}{i}) (p \mod i)^{-1}$; 然后我们就可以推出逆元了,代码只有一行: ```cpp a[i]=-(p/i)*a[p%i]; ``` 但是,有些情况下要避免出现负数,所以我们要改改代码,让它只求正整数: ```cpp a[i]=(p-p/i)*a[p%i]%p; ``` 这就是线性求逆元 ## 逆元练习题 [[AHOI2005]洗牌](https://www.lydsy.com/JudgeOnline/problem.php?id=1965) [[SDOI2016]排列计数](https://www.luogu.org/problemnew/show/P4071) Loading
docs/math/inverse.md +95 −0 Original line number Diff line number Diff line [【模板】乘法逆元](https://www.luogu.org/problemnew/show/P3811) ## 逆元简介 如果一个线性同余方程 $ax \equiv 1 \pmod b$,则 $x$ 称为 $a$ 的逆元,记作 $a^{-1}$。 ## 如何求逆元 ### 扩展欧几里得法: ```cpp void exgcd(int a,int b,int c,int &x,int &y){ if(a==0){ x=0; y=0; return; } else{ int tx,ty; exgcd(b%a,a,tx,ty); x=ty-(b/a)*tx; y=tx; return; } } ``` 扩展欧几里得法和求解线性同余方程是一个原理,在这里不做解释。 ### 快速幂法: 这个要运用费马小定理: > 若 $p$ 为质数,$a$ 为正整数,且 $a$、$p$ 互质,则 $a^{p-1} \equiv 1 \pmod p$。 关于费马小定理在[这里]() 因为 $ax \equiv 1 \pmod b$; 所以 $ax \equiv a^{b-1} \pmod b$(根据费马小定理); 所以 $x \equiv a^{b-2} \pmod b$; 然后我们就可以用快速幂来求了。 代码: ```cpp #define ll long long inline ll poW(ll a,ll b){ long long ans=1; a%=p; while (b){ if (b&1) ans=((ans*a)%p+p)%p; a=(a*a)%p; b>>=1; } return ans%p; } ``` ### 线性求逆元 但是如果要求的很多,以上两种方法就显得慢了,很有可能超时,所以下面来讲一下如何线性求逆元。 首先,很显然的 $1^{-1} \equiv 1 \pmod p$; 然后,设 $p=ki+j,j<i,1<i<p$,再放到 $\mod p$ 意义下就会得到:$ki+j \equiv 0 \pmod p$; 两边同时乘 $i^{-1},j^{-1}$: $kj^{-1}+i^{-1} \equiv 0 \pmod p$; $i^{-1} \equiv -kj^{-1}+ \pmod p$; $i^{-1} \equiv -(\frac{p}{i}) (p \mod i)^{-1}$; 然后我们就可以推出逆元了,代码只有一行: ```cpp a[i]=-(p/i)*a[p%i]; ``` 但是,有些情况下要避免出现负数,所以我们要改改代码,让它只求正整数: ```cpp a[i]=(p-p/i)*a[p%i]%p; ``` 这就是线性求逆元 ## 逆元练习题 [[AHOI2005]洗牌](https://www.lydsy.com/JudgeOnline/problem.php?id=1965) [[SDOI2016]排列计数](https://www.luogu.org/problemnew/show/P4071)