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;
}

 

 

 做法四(阶乘逆元法):

 

posted @ 2021-03-12 19:18  mikku  阅读(100)  评论(0)    收藏  举报