咱們首先來看個線性同餘方程:html
若是對於方程 ax = b(a不爲0),因爲a存在倒數,所以很容易求解。若是在mod m的運算下,也有知足這樣a的倒數同樣的數存在的話,方程就有解了。而這個解x就叫作a關於m的逆元,記作或是inv(a)。若是能求出逆元,那麼就有x = inv(a) * ax = inv(a) * b, 就能夠求出x了。算法
那麼咱們怎麼求出inv(a)呢?spa
其實就是解htm
咱們設ax = mt + 1;blog
移項得ax - mt = 1;get
咱們設y = -m,得ax + my = 1;it
咦,這方程怎麼那麼眼熟,好像可用擴展歐幾里得算法求得。 class
固然對於方程知足的條件必須是gcd(a, m) = 1,不然的話逆元是不存在的。擴展
附上僞代碼:gc
int gcd(int a, int b){ return !b ? gcd(b, a % b) : a; } int extgcd(int a, int b, int& x, int& y){ int d = a; if(b != 0){ d = extgcd(b, a % b, y, x); y -= (a / b) * x; } else x = 1, y = 0; return d; } int inv(int a, int m){ int x, y; int d = extgcd(a, m, x, y); if(gcd(a, m) == 1)return (m + x % m) % m; else return -1;//-1表示不存在逆元 }
固然,逆元還有其餘的求法。在這以前咱們須要知道一些姿式。
費馬小定理:
在p是素數的狀況下,對任意整數x都有
其中若是x不能被p整除則有:
繼續變形:
咦,咱們發現就是x關於p的逆元。即:
所以就能夠用矩陣快速冪運算來求出逆元。
在不是素數的狀況下, 咱們也能夠經過歐拉定理來求解。
歐拉定理:若a, n均爲正整數,且a, n互質,則
而費馬小定理僅僅是其一個特例而已。
固然, 咱們還能夠用另外的方法。
咱們知道p % b = p - (p / b) * b(這裏的/表示整數除法ex : 7 / 2 = 3)
設x = p % b, y = p / b;
因而就有x + by = p;
咱們兩邊同時取餘p得(x + by) % p = 0;
x % p = (-y) * b % p;
x * inv(b) % p = (-y) % p;
inv(b) = (-y) * inv(x) % p;
inv(b) = (p - y) * inv(x) % p;
將x, y代入得:
inv(b) = (p - p / b) * inv(p % b) % p;
附上僞代碼:
const int MOD = (int)1e9 + 7;//按題目要求的取餘數 const int N = 1000000 + 5; int inv[N + 5]; void init_inv(){ inv[1] = 1; for(int i = 2; i < N; i ++) inv[i] = (MOD - MOD / i) * 1ll * inv[MOD % i] % MOD; }