P3811 乘法逆元

传送

乘法逆元:ax ≡ 1 (mod p),其中x为a的逆元,求模意义下的乘法逆元,通常有一下几种方法:

1.拓展欧几里得(也就是exgcd)

  ax ≡ 1 (mod p)

 ax-py=1

这就变成解不定方程的问题了,根据拓展欧几里得算法,代码如下(会TLE3个点)(就算开o2优化也没有卵用)

#include<iostream>
#include<cstdio>
using namespace std;
long long n,p;
void exgcd(long long a,long long b,long long &d,long long &x,long long&y)//其中d为a,b的最大公约数
{if(b==0){x=1;y=0;d=a;//当然,exgcd 也可以写成water lift 大佬的有返回值的
}
  else{
      exgcd(b,a%b,d,y,x);
      y-=a/b*x;
  }
}
int main()
{  
    cin>>n>>p;
    for(long long i=1;i<=n;i++)
    {  long long x,y,d;
        exgcd(i,p,d,x,y);
        cout<<((x/d)%(p/d)+(p/d))%(p/d)<<endl;//防止x为负数
    }
}

2.费马小定理(因为数据保证p为质数)

 a^(p-1) ≡ 1 (mod p)

 a*a^(p-2) ≡ 1(mod p)

所以a^(p-2)即为a的逆元。

n个月过后来补个锅(这玩意用快速幂做)

代码:

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int a,p;
int ksm(int a,int b,int p)
{int r=1;
   while(b)
   {if(b&1)r=r*a%p;
     a=a*a%p;
     b/=2;
   }
   return r;
}
int main()
{
    scanf("%d%d",&a,&p);
    cout<<ksm(a,p-2,p);
}

不过依旧会TLE

3.说了这么多,终于说到不TLE的解法了

  那就是线性递推

 设p=k*a+r,

  k*a+r ≡ 0(mod p)

  k*a ≡ -r (mod p)

  两边同乘(-r^(-1))*(a^(-1))

  k*(-r)^(-1) ≡ a^(-1)  (mod p)

  即-k*r^(-1) ≡ a^(-1) (mod p)

   k=p/a(向下取整)

   这样,就得到了a在模p意义下的逆元

   递推式 inv[i]=-(p/a)*inv[p%a]%p,

   为了让结果不是负数,递推式就变为 inv[i]=(p-p/a)*inv[p%a]%p,

   代码如下   

#include<iostream>
#include<cstdio>
using namespace std;
long long n,p,f[3000001];
int main()
{  
    scanf("%lld %lld",&n,&p);//要用scanf和printf,不然会超时
    f[0]=1;f[1]=1;
    printf("%lld \n",f[1]);
    for(long long i=2;i<=n;i++)
    {  f[i]=(long long)((p-p/i)*f[p%i])%p;
      printf("%lld \n",f[i]);
    }
    
}

 

转载于:https://www.cnblogs.com/lcez56jsy/p/10560728.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值