数论学习笔记
快速幂
快速幂可以用 \(O(\log b)\) 的时间复杂度求解 \(a^b \bmod\ p\)。
每次检查当前 \(b\) 二进制下的最后一个为是否为 \(1\),为 \(1\) \(ans\) 就乘上当前的 \(a\)。每次需要将 \(a\) 乘上 \(a\),并将 \(b\) 右移一位。
没什么例题可讲,直接放代码。
代码:
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
int a, b, mod;
int qpow(int a, int b)
{
int ans = 1;
while(b)
{
if(b & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ans;
}//快速幂
signed main()
{
cin >> a >> b >> mod;
cout << a << '^' << b << " mod " << mod << '=' << qpow(a, b);
return 0;
}
逆元
求逆元通常有以下 \(3\) 种方法:
- 费马小定理:若模数 \(p\) 为质数,则 \(x\) 在模 \(p\) 意义下的乘法逆元为 \(x ^ {p - 2} \bmod\ p\)。
- 扩展欧几里得:\(a\) 在模 \(b\) 意义下的乘法逆元相当于的同余方程 \(a x \equiv 1 \pmod {b}\) 的最小正整数解,用扩欧求解。
- 递推求逆元:\(inv_x\) 表示 \(x\) 在模 \(p\) 意义下的乘法逆元,则 \(inv_x = (p - \frac{p}{x})\times inv_{p \bmod\ x}\bmod\ p\)。
例题
逆元模板,但是费马小定理和扩展欧几里得求逆元会超时,需要用 \(O(n)\) 的递推求逆元方法。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e6 + 10;
int inv[N];
int n, p;
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> p;
inv[1] = 1;
for(int i = 2;i <= n;i ++) inv[i] = (p - p / i) * inv[p % i] % p;//递推式
for(int i = 1;i <= n;i ++) cout << inv[i] << '\n';
return 0;
}
Lucas 定理
若 \(n,k\) 为正整数,\(p\) 为质数:
\[\binom{n}{k} \equiv \binom{n \bmod p}{k \bmod p} \times \binom{\left\lfloor n/p \right\rfloor}{\left\lfloor k/p \right\rfloor} \pmod{p}
\]
例题
Lucas 定理模板,预处理出阶乘和逆元然后直接套模板。
#include <bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
const int N = 1e5 + 10;
int n, m, p;
int fact[N], inv[N];//fact 数组是阶乘,inv 数组是逆元
int qpow(int a, int b, int mod)
{
int ans = 1;
while(b)
{
if(b & 1) ans = (ans * a) % mod;
a = (a * a) % mod;
b >>= 1;
}
return ans;
}//快速幂
int C(int n, int k, int mod)
{
if(k < 0 || k > n) return 0;
return fact[n] * inv[k] % mod * inv[n - k] % mod;
}//计算组合数
int Lucas(int n, int k, int mod)
{
if(k == 0) return 1;
return C(n % mod, k % mod, mod) * Lucas(n / mod, k / mod, mod) % mod;
}//Lucas 定理模板
signed main()
{
int T;
cin >> T;
while(T --)
{
cin >> n >> m >> p;
fact[0] = 1;
inv[0] = 1;
for(int i = 1;i <= N - 10;i ++)
{
fact[i] = fact[i - 1] * i % p;
inv[i] = qpow(fact[i], p - 2, p);
}
cout << Lucas(n + m, n, p) << '\n';
}
return 0;
}

浙公网安备 33010602011771号