引入
首先,我们来想一个问题:4/2 mod 3=? (⊙o⊙)…多少有点弱智了。很明显,是2。但在实际过程中,每一步都要取模,然后就出问题了。比如你把4拆了,拆成2+2,然后模3,变成1,然后除2,再模3,就成了......?????????????什么玩意儿啊?
所以,我们就需要逆元这个玩意儿。
扩展欧几里得
当,则称x是a的逆元。这个式子,有点熟悉,同余方程!
于是,我们就可以用扩展欧几里得求逆元了!!!(证明在数论基础中有介绍)
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;
y=0;
return a;
}
int res=exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return res;
}
int inverse(int n,int mod){
int x,y;
int _=exgcd(n,mod,x,y);
x%=mod;
if(x<0)
x+=mod;
return x;
}
很显然,逆元不止这些求法。
费马小定理(快速幂)
还是那个式子:,然后由费马小定理可得
。
所以,。然后,我们就可以用快速幂求
,也就是逆元了。
int fast_pow(int base,int n){
int res=1;
while(n){
if(n&1)
res=res*base%p;
base=base*base%p;
n>>=1;
}
return res;
}
int inverse(int x){
return fast_pow(x,p-2);
}
线性求逆元
由于up太菜,看不懂,只好放个传送门和两张图片了。
一种新方法
这个方法比较有意思。它先枚举出1~n的阶乘,然后只算n的阶乘的逆元。接着从后往前递推,算出1~n的所有阶乘的逆元。怎么算?
首先,我们知道。(证明:两边同乘一个ab就结束了)
然后,我们令inv表示阶乘的逆元,f是阶乘,所以就得出了下面这一大坨东西↓
所以我们就得到了,因为n和
抵消。
SO,根据这个,。\ohhhhhhhhhhhhhhhhhhhhhhhhh
一道模板题:P3811
现在就很简单啦,线性求逆元和新方法随你用。
#include <bits/stdc++.h>
using namespace std;
const int maxn=3000005;
long long factorial[maxn],invf[maxn];
long long exgcd(long long a,long long b,long long &x,long long &y){
if(b==0){
x=1;
y=0;
return a;
}
long long res=exgcd(b,a%b,x,y);
long long tmp=x;
x=y;
y=tmp-a/b*y;
return res;
}
long long inverse(long long n,long long mod){
long long x,y;
long long _=exgcd(n,mod,x,y);
x%=mod;
if(x<0)
x+=mod;
return x;
}
int main(){
int n,p;
cin>>n>>p;
factorial[0]=1;
for(int i=1;i<=n;i++)
factorial[i]=factorial[i-1]*i%p;
invf[n]=inverse(factorial[n],p);
for(int i=n-1;i>=0;i--)
invf[i]=invf[i+1]*(i+1)%p;
for(int i=1;i<=n;i++)
cout<<invf[i]*factorial[i-1]%p<<endl;
return 0;
}
但是要写scanf和printf,不然会T。即使加上了这些↓也会TLE。(亲身经历)
#pragma GCC optimize(3) //吸氧,O3优化
ios::sync_with_stdio(false); //cin/cout加速器
当然,求单个逆元也可以用快速幂,都是log级别,随便你。
逆元的应用有很多,具体遇到了再说,就不放在本篇里了。
ok,以上就是本期的全部内容了。我们下期再见!(~ ̄(OO) ̄)ブ
友情提示:请勿复制P3811的代码