逆元:在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∗b ≡ 1 ( mod p ) 变形为 a∗b + k∗p = 1
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; }
二:由费马小定理 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); }
三:递推求逆元 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[p mod i];
ll inv[mod+5]; //线性求逆元 void getinv(ll mod) { inv[1]=1; for(int i=2;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/2^n
输出答案为:2*inv(2^(n-1))%mod
因为mod=1e9+7是个素数,跟2互质
可以使用费马小定理降幂,在用费马小定理求逆元
或者直接用快速幂降幂取模,不过最后乘逆元可能会爆,可以用快速乘
费马小定理降幂的求法,可以适用于指数特别大的情况,这题指数只到1e18(出题人太善良了)
#include#include 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 ) { 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 10+s[i]-'0')%mod; sum=getInv(sum,mod); ll ans=n*sum%mod; cout< endl; } return 0; }