同餘式:
設m是給定的一個正整數,a、b是整數,若滿足m|(a-b),則稱a與b對模m同餘,記爲a≡b(mod m),或記爲a≡b(m)。這個式子稱爲模m的同餘式。
a≡b(mod m) 等價於 a,b分別除以m,得到的餘數相同。
乘法逆元
概念:如果ax ≡ b (mod p) ,且gcd(a, p) = 1(a, p互質,是逆元存在的充要條件), 則稱a 的逆元爲 x。
逆元的含義:在模以p的條件下,一個數 a 如果有逆元 x 存在, 那麼除以 a 相當於乘以 x。
求解逆元的方法(只介紹常用的有兩種,其他方法自行尋找):
1.拓展歐幾里得算法(算法證明請點擊我另外一篇博客)
簡單說明一下,就是有兩個整數a, b,存在有x,y 使滿足貝祖等式 ax + by = gcd(a, b).
求得的 x 和 y(求得的其中一個很可能是負數)。
當a關於模以b的逆元存在,有gcd(a, b) = 1, 原式化簡爲: ax + by = 1,
方程兩邊同時模以b:
ax % b + by % b = 1 % b
ax % b = 1 % b
ax ≡ 1(mod b)
所以a的乘法逆元爲x, 同理b的乘法逆元爲y
因爲x或y有可能是負數,所以a的逆元爲 (x % p + p) % p, b的逆元爲 (y % p + p) % p.
代碼實現
int exgcd(int a, int b, int &x, int &y) { //返回gcd(a, b)
if (!b) {
x = 1;
y = 0;
return a;
}
int res = exgcd(b, a % b, y, x);
y -= a / b * x;
return res;
}
int main() {
int a, b, x, y;
cin >> a >> b;
cout << exgcd(a, b, x, y) == 1 ? (x % b + b) % b : 0 << endl; //0代表逆元不存在
return 0;
}
時間複雜度O(lg(mod)) , mod爲模的大小
2.費馬小定理
假設a是一個整數,p是一個質數,而整數a不是p的倍數,那麼gcd(a, p) = 1.
則有 a^(p-1)≡1(mod p)
所以 a * a ^(p-2) ≡ 1 (mod p)
所以a關於模p的逆元爲 x = a^(p-2) (mod p),用快速冪即可解決
代碼如下
typedef long long ll;
ll pow_mod(ll a, ll b, ll mod) {
ll res = 1;
while (b) {
if (b & 1)
res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
ll solve(ll a, ll p) { //費馬小定理求a關於b的逆元
return pow_mod(a, p - 2, p);
}
時間複雜度O(lg(mod))
組合數運算
公式:
我們的目標就是求取 的值
一.運用逆元或者拓展歐幾里得算法
1.求取1到n的階乘對mod取模的結果存入數組ans[]中
2.求取 時,先利用“拓展歐幾里得算法”或者“費馬小定理+快速冪”,求出ans[r]的逆元存入臨時變量 a1。
3.計算ans[] * a1 % mod 存入臨時變量a2 (a2 即爲),(式子不理解的話看上面逆元的含義)
4.求取ans[n-r]的逆元存入臨時變量a3
5.
一開始求取ans[]的時間複雜度O(n),求m次組合數總的時間複雜度爲O(mnlg(mod)).
代碼如下:
void init(ll mod) { //第一步
for (int i = 1; i <= n; i++)
ans[i] = ans[i-1] * i % mod;
}
ll pow_mod(ll a, ll b, ll mod) {
ll res = 1;
while (b) {
if (b & 1)
res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return res;
}
ll niyuan(ll a, ll b) {
return pow_mod(a, b - 2, b);
}
ll C(ll a, ll b, ll mod) { //計算C(a, b) % mod
return ans[a] * niyuan(ans[b], mod) % mod
* niyuan(ans[a-b], mod) % mod;
}
二,運用盧卡斯定理(Lucas)進行求解
證明的話自行百度吧。。自己會用就好了。。
核心內容就是一條式子:
其中n,m很大,而p相對而言很小。
代碼如下:
ll Lucas(ll n, ll m, ll mod) {
return m ? Lucas(n / mod, m / mod, mod) * C(n % mod, m % mod, mod) % mod : 1;
}