zoukankan      html  css  js  c++  java
  • 数论倒数 : 逆元

    逆元:在mod意义下,不能直接除以一个数,而要乘以它的逆元 

    a*b≡1(mod p) ,a,b互为模n意义下的逆元

    一个数有逆元的充分必要条件是gcd(a,mod)=1

    ( a + b ) % p = ( a % p + b % p ) % p ;  (正确)

    ( a - b ) % p = ( a % p - b % p ) % p ;  (正确)

    ( a * b ) % p = ( a % p * b % p ) %p  (正确)

    ( a / b ) % p = ( a % p / b % p) % p  (错误)

    a的逆元,用inv(a)表示

    则:( a / b ) % p = ( a * inv(b) ) % p = ( a % p * inv(b) % p ) % p

     

    一:扩展欧几里得求逆元 O(log n)

    a≡ mo变形为 ak1

     

    ll exgcd(ll a,ll b,ll &x,ll &y) {
        if(b==0) {
            x=1,y=0;
            return a;
        }
        ll ret=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return ret;
    }
    //求a在mod下的逆元,不存在逆元返回-1 
    ll getinv(int a,int mod) {
        ll x,y;
        ll d=exgcd(a,mod,x,y);
        return d==1?(x%mod+mod)%mod:-1;
    }
    View Code

    二:由费马小定理 a^(p-1) ≡ 1 (mod p)    O( log mod )  

    适用条件:p是个素数

    即: a(p-2) * a ≡ 1 (mod p) 两边同除a 

    得:a(p-2) ≡ inv(a) (mod p)

    Inv(a) = a(p-2) (mod p)

    ll pow(ll a,ll b,ll mod) {
        ll ans=1;
        while(b) {
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    ll getinv(ll a,ll mod) {
        return pow(a,mod-2,mod);
    }
    View Code

    三:递推求逆元  O(log mod)

    令p=k*i+r,则k=p/i,r=p%i;

    k*i+r≡0(mod p)

    i(-1)≡-k*r(-1)(mod p)

    即: inv[i] ≡ −(p/i) * inv[moi];

    ll inv[mod+5]; //线性求逆元
    void getinv(ll mod) {
        inv[1]=1;
        for(int i=2;i<mod;i++)
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }

     四:递归求逆元 O(log mod)

    ll inv(ll i) {
        if(i==1) return 1;
        return (mod-mod/i)*inv(mod%i)%mod;
    }

     例题:EOJ月赛

    https://acm.ecnu.edu.cn/contest/196/problem/D/

    求园内所有点都在同一个半圆内的概率

    推公式:在圆内,先随机定一个点a 做关于圆心对称点a' ,连接两点,直线的中垂线即把圆分为两半,该线为分割线, 由分割线对每个点做出对称点,这样一共由2n个点,接下来问题变为:在2n个点中取n个点,其中对于每个点和它的对称点,只能选一个出来。一共就由2^n种选法,接下来求在2^n种选法中有多少种选法使n个点有公共交集,(点所在的半圆即为他的集)一共有2n种选法,所以概率为:2n/2n

    输出答案为:2*inv(2(n-1))%mod

    因为mod=1e9+7是个素数,跟2互质

    可以使用费马小定理降幂,在用费马小定理求逆元

    或者直接用快速幂降幂取模,不过最后乘逆元可能会爆,可以用快速乘

    费马小定理降幂的求法,可以适用于指数特别大的情况,这题指数只到1e18(出题人太善良了)

    #include <iostream>
    #include <cstring>
    using namespace std;
    typedef long long ll;
    const ll mod=1e9+7;
    
    const int maxn=1e6+5;
    char s[maxn];
    
    ll ksm(ll a,ll b) {
        ll ans=1;
        while(b){
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;
            b=b>>1;
        } 
        return ans;
    }
    
    ll fun(char *s){
        ll ans=0;
        int len=strlen(s);
        for(int i=0;i<len;i++) {
            ans=(ans*10+s[i]-'0') %(mod-1);
        }
        return ans;
    }
    
    ll qkpow(ll a,ll b,ll mod) {
        ll ans=1;
        a=a%mod;
        while(b) {
            if(b&1) ans=ans*a%mod;
            a=a*a%mod;
            b>>=1;
        }
        return ans;
    }
    ll getInv(ll a,ll mod) {
        return qkpow(a,mod-2,mod);
    }
    int main( ) {
        int t;
        cin>>t;
        while(t--) {
            cin>>s;
            ll sum=fun(s)-1;
            sum=ksm(2,sum);
            
            ll n=(s[0]-'0')%mod;
            for(int i=1;i<strlen(s);i++) n=(n*10+s[i]-'0')%mod;
            
            sum=getInv(sum,mod);
            ll ans=n*sum%mod;
            cout<<ans<<endl;
        }
        return 0;
    }
    View Code
  • 相关阅读:
    3(翻译)如何在cocos2d里面使用动画和spritesheet
    Objectivec2.0 每种数据类型定义属性的方法
    cocos2d 入门必备4个基本概念
    如何在Mac上搭建自己的服务器——Nginx
    JN_0001:在微信朋友圈分享时长大于10s的视频
    JN_0002:Win10禁止U盘拷贝文件的方法
    abstract class 和 interface区别
    ref和out
    .Net配置错误页
    Unity3d 物体沿着正七边形轨迹移动
  • 原文地址:https://www.cnblogs.com/iwomeng/p/11377114.html
Copyright © 2011-2022 走看看