P3811 【模板】乘法逆元 (*线性递推,费马小定理,同余方程)
首先了解乘法逆元的意义。
乘法逆元即a*x=1(mod p).a的逆元就是那个x
如果我们有ans=(a*b*c)mod p
那么如果我们要得到(b*c)mod p.则不能简单的除a。
而要让(ans*x) mod p.从而消除a的影响。
做法一:
同余方程(TLE__555ms)(且模数可以不为质数):
同余方程详情见上二元一次方程求解(扩展欧几里得)
代码如下:
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #define MAXN 505 using namespace std; typedef pair<int,int> pii; typedef long long ll; int n,p,x,y;//x,y为真正的解 void exgcd(int i,int p){ if(p==0){ x=1; y=0; return; } exgcd(p,i%p); int temp =x; x=y; y=temp-i/p*y; } int main(){ scanf("%d%d",&n,&p); for(int i=1;i<=n;i++){ exgcd(i,p); x=(x%p+p)%p; printf("%d\n",x); } return 0; }
做法二:
费马小定理(TLE___700ms)(模数需为质数):
ap-1=1( mod p)(p为质数)
那么就可以知道a*ap-2=1(mod p).
即(ap-2)mod p 为a的逆元。之后用快速幂求解即可。
TL代码如下orz:
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #define MAXN 505 using namespace std; typedef pair<int,int> pii; typedef long long ll; ll n,p,ans; void Quick_pow(ll a,ll p){ int temp=p-2; ans=1; a%=p; while(temp){ if(temp&1) ans=(a*ans)%p; a=(a*a)%p; temp>>=1; } } int main(){ scanf("%lld%lld",&n,&p); for(int i=1;i<=n;i++){ Quick_pow(i,p); printf("%lld\n",ans%p); } return 0; }
做法三:
此时,我们发现前两个代码TLE的原因都是因为求1~n。需要计算太多次。同时我们发现1~n为线性。那么有没有一种线性递推的方法来帮助解题呢
答案是有的!(这个写公式的格式什么的太麻烦了。就从题解里掏公式和相应证明吧!)

即公式为
inv[i]=(ll)(p-(p/i))*inv[p%i]%p
AC代码如下(225ms):
#include<cstdio> #include<algorithm> #include<cstring> #include<queue> #include<vector> #define MAXN 3000005 using namespace std; typedef pair<int,int> pii; typedef long long ll; int n,p,ans[MAXN]={0,1};//x,y为真正的解 int main(){ scanf("%d%d",&n,&p); printf("1\n"); for(int i=2;i<=n;i++){ ans[i]=(ll)(p-(p/i))*ans[p%i]%p; printf("%d\n",ans[i]); } return 0; }
做法四(阶乘逆元法):


浙公网安备 33010602011771号