zoukankan      html  css  js  c++  java
  • 乘法逆元及两道模板题详解

    乘法逆元

    就是a	imes bequiv 1(mod pleftleftleft)

    此时b就是a模p意义下的逆元,即inv[a]=b;

    下面我们用inv[a]表示a模p意义下的逆元。

    逆元是好东西啊

    有时候我们需要算出 a/b mod p 的值,用朴素的方法,我们只能在 a 上不断加 p ,直到它能被 b 整除为止。
    当 a,b,p 都很大的时候,这种方法就只能凉凉了,但如果有了逆元,我们就可以非常方便,快捷地求解。                    
     
                                                                          ——————某位大佬的话

    所以我先讲讲逆元性质:

    唯一性就不用讲了

    1.积性

    假如a与b互质,inv[a]	imes inv[b]equiv inv[a*b]

    2.乘变除

    a	imes inv[b]equiv adiv b (mod pleftleft) 

    证明如下:

    b	imes inv[b]equiv 1left ( mod p leftleft)          

    两边都乘一个 frac{a}{b} ,就得到了上面的式子


    至于逆元的求法,有很多,我只讲四种。

    首先,是求单个逆元

    1.费马小定理

    当 p 为素数时:

    a^{p-1}equiv 1(mod pleftleftleft)

    a^{p-1}= a	imes a^{p-2}

    所以

    a	imes a^{p-2}equiv 1(mod pleftleftleft)

    所以 a^{p-2}就是a在模p意义下的逆元,快速幂求出 a^{p-2}即可。

    此方法的局限就是p只能是质数。

     1 int ksm(int t)
     2 {
     3     if(t==1)return n%p;
     4     if(t%2==0)
     5     return ksm(t>>1)*ksm(t>>1)%p;
     6     else
     7     return ksm(t>>1)*ksm(t>>1)*(n)%p;
     8 }
     9 int main()
    10 {
    11     scanf("%d%d",&n,&p);
    12     printf("%d",ksm(p-2));
    13 }

    2.扩展欧几里得

    a	imes inv[a]equiv 1(mod pleftleftleft)

    变一下,我们把p移到左边就成了

    a	imes inv[a]-py= 1

    因为y是变量所以可以变成这样:

    a	imes inv[a]+py= 1

    我们把inv[a]设成x,就变成了扩欧的标准式:

    ax+py= 1

    就可以瞎搞了,至于不知道怎么瞎搞的同学,可以先点这里exgcd入门以及同余基础

     1 inline void exgcd(ll a,ll b)
     2 {
     3     if(!b)
     4     {x=1;y=0;return;}
     5     exgcd(b,a%b);
     6     k=x;x=y;
     7     y=k-a/b*y;
     8     return;
     9 }
    10 int main()
    11 {
    12     scanf("%d%d",&n,&p);
    13     exgcd(n,p);
    14     printf("%d",x);
    15 }

    然后,就是求多个逆元

    1.欧拉函数筛法

    在数论,对正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目(φ(1)=1)。
                                                             ——————百度百科

    我们设varphi(x)表示小于等于x且与x互质的正整数的个数。

    通式如下:

    还有欧拉函数有如下几点性质:

    1. 欧拉函数是积性函数——若m,n互质,
    2. 假如n为奇数,
    3. 假如n为质数,

    欧拉函数与费马小定理有点不明不白的关系

    对任何两个互质的正整数a, m(m>=2)有

    这就是欧拉定理

    所以类比于费马小定理的证法:

    a^{phileft (m 
ight )}equiv 1(mod pleftleftleft)

    a^{phileft (m 
ight )}= a	imes a^{phileft (m 
ight )-1}

    所以a	imes a^{phi(m)-1} equiv 1(mod pleftleftleft)

    所以我们要求的就是a^{phi(m)-1},又因为欧拉函数是积性函数,所以把m筛一下质数就好了。

    不过有更好的办法,这里就不写代码了(其实不会

    2.线性递推

    证明好麻烦啊,那就不证了

    反正就是设p= b	imes k+a

    再往a	imes inv[a]equiv 1(mod pleftleftleft)

    最后得出:-(frac{p}{a})*inv[pmod a]equiv inv[a]


    讲完了,现在讲题目

    我要讲洛谷的两道题:

    1.P3811

    这就是模板题,直接套线性递推,就能ac了

     1 int main()
     2 {
     3     int n,m;
     4     read(n),read(m);
     5     inv[1]=1;
     6     for(register int i=2;i<=n;i++)
     7     inv[i]=(long long)(m-m/i)*inv[m%i]%m;\加一个m是为了去负
     8     for(register int i=1;i<=n;i++)
     9     printf("%d
    ",inv[i]);
    10 }

      值得一提的是,这题扩欧卡一卡也可以过(好水

     1 long long k,x,y;
     2 inline void read(long long &x)
     3 {
     4     x=0; 
     5     int f=1; 
     6     char ch=getchar();
     7     while(ch<'0'||ch>'9')
     8     {
     9         if(ch=='-') 
    10         f=-1; 
    11         ch=getchar();
    12     }
    13     while(ch>='0'&&ch<='9')
    14     {
    15         x=x*10+ch-'0';
    16         ch=getchar();
    17     }x*=f;
    18 }
    19 inline void exgcd(int a,long long b)
    20 {
    21     if(b==0)
    22     {x=1;y=0;return;}
    23     exgcd(b,a%b);
    24     k=x;x=y;
    25     y=k-a/b*y;
    26     return;
    27 }
    28 int main()
    29 {
    30     long long n,m;
    31     read(n),read(m);
    32     for(register int i=1;i<=n;i++)
    33     {
    34         exgcd(i,m);
    35         if(x<=0)x+=m;
    36         printf("%lld
    ",x);
    37     }
    38 }

    对了,还有一个,费马小定理算法。

    得分48,a了3个,t了3个

     1 inline long long ksm(int t,int i)//记得开long long不然第三个点会wa
     2 {
     3     if(t==1)return i%m;
     4     if(t%2==0)
     5     return ksm(t>>1,i)*ksm(t>>1,i)%m;
     6     else
     7     return ksm(t>>1,i)*ksm(t>>1,i)*(i)%m;
     8 }
     9 int main()
    10 {
    11     long long n;
    12     io::begin();
    13     io::read(n);
    14     io::read(m);
    15     for(register int i=1;i<=n;i++)
    16     printf("%lld
    ",ksm(m-2,i));
    17 }

    但是我发现,去掉 inline,就a了4个点,得分64.

    所以给大家一个忠告,在递归函数下,inline不是一个好的卡常方法!

    因为使用内联函数后虽然调用函数的开销降低了,但是有利必有弊,内联函数会导致主函数指令增多、函数体积增大等情况。

    这题就这样

    2.P2613

    一看数据范围还是很吓人的,10^{10001},怕不是要打高精,后来发现不是这样,不过题解中真有高精算法,大佬!!

    首先,先看我最开始引用的话,嗯,有点道理

    frac{a}{b}=a	imes b^{-1}

    然后解析题目发现:本题就是求b在mod p的意义下的逆元。

    所以扩欧一遍过,只是读入时注意一下边读边模就好了;

     1 inline void exgcd(int a,int b)
     2 {
     3     if(!b)
     4     {x=1;y=0;pos=a;return;}
     5     exgcd(b,a%b);
     6     k=x;x=y;
     7     y=k-a/b*y;
     8     return;
     9 }
    10 int main()
    11 {
    12     std::cin>>a1>>b1;
    13     int n=0,m=0;
    14     for(register int i=0;i<strlen(a1);i++)
    15     n=(n*10+a1[i]-48)%mod;
    16     for(register int i=0;i<strlen(b1);i++)
    17     m=(m*10+b1[i]-48)%mod;
    18     exgcd(m,mod);
    19     if(pos!=1)printf("Angry!");
    20     else
    21     {
    22         if(x<0)x+=mod;
    23         printf("%lld",(1ll*n*x)%mod);
    24     }
    25 }

    最后通过这道题的大质数给大家念一首诗:苟利国家生死以

  • 相关阅读:
    linux-01Red Hat Enterprise Linux 7(RHEL7)配置静态IP地址
    Linux下Tomcat重新启动
    获取root权限
    -save 和 --save-dev的区别
    周五的听歌时间
    十月第二周计划
    国庆计划
    周末计划
    前端基础进阶系列
    前端基础进阶(二):执行上下文详细图解
  • 原文地址:https://www.cnblogs.com/mashiro-/p/9526398.html
Copyright © 2011-2022 走看看