Loading

数论倒数 : 逆元

逆元:在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

 

posted @ 2019-08-19 14:58  qinuna  阅读(220)  评论(0编辑  收藏  举报