数论2:同余,欧拉函数,逆元
数论2:同余,欧拉函数,逆元
同余
公式三证明:
公式三更一般的结论:
线性同余方程
对 \(mx\equiv 0(\mathrm{mod\,\,}m),ax\equiv b(\mathrm{mod\,\,}m)\) 辗转相除
还有一种计算方法比较好理解(来自dls)
解线性同余方程板子:
// 求 a * x = b (mod m) 的解
ll modequ(ll a, ll b, ll m) {
ll x, y;
ll d = exgcd(a, m, x, y);
if (b % d != 0) return -1;
m /= d; a /= d; b /= d;
x = x * b % m;
if (x < 0) x += m;
return x;
}
简化剩余系
所有的 n 满足 \(0<n\leq m,(n, m)=1\) 构成了一个模 m 的简化剩余系。\(\phi(m)\) 表示 n 的个数。
欧拉函数
欧拉函数证明
欧拉函数板子
ll phi (ll n) {
ll ans = n;
for (int i = 2; i*i <= n; i++) {
if (n % i == 0) {
ans = ans / i * (i - 1); //*(1-1/i)
while (n % i == 0) n /= i;
}
}
if (n > 1) ans = ans / n * (n - 1); //还剩下就再乘
return ans;
}
欧拉定理
若 \((a,m)=1\),则 \(a^{\phi(m)}\equiv1(\mathrm{mod\,\,}m)\)
m为素数时有 费马小定理:\(a^{p-1}\equiv1(\mathrm{mod\,\,}p)\)
欧拉定理证明
设 \(1-n\) 中与 \(n\) 互质的数为:\(p_1, p_2, ..., p_{\phi(n)}\),则有 \(ak_1,ak_2,...sk_{\phi(n)}\),二者在模n意义下等价(可以手搓几个例子模拟),
二式分别累乘,有 \(k_1k_2...k_{\phi(n)}\equiv a^{\phi(n)}k_1k_2...k_{\phi(n)} (\mathrm{mod\,\,}n)\),即
根据同余公式三 \(ka\equiv ka'(\mathrm{mod\,\,} m)\rightarrow a\equiv a'(\mathrm{mod\,\,} \frac m{(m,k)})\)
则有
逆元
\(ax\equiv1(\mathrm{mod\,\,}m)\),称 \(x\) 为 \(a\) 的逆元,记作 \(a^{-1}\)
快速幂求逆元模板
素数:
\(a^{p-1}\equiv1(\mathrm{mod}\,\,p)\rightarrow aa^{p-2}\equiv1(\mathrm{mod\,\,p)\rightarrow a^{p-2}\equiv a^{-1}(\mathrm{mod}\,\,p})\)
ll qmi(ll a, ll k, ll p) {
ll ans = 1;
while (k) {
if (k & 1) ans = ans * a % p;
k >>= 1;
a = a * a % p;
}
return ans;
}
//a mod p 的逆元为 qmi (a, p - 2, p)
扩展欧几里得求逆元模板
非素数:
\(a^{\phi(m)}\equiv1(\mathrm{mod\,\,}m)\rightarrow a^{\phi(m)-1}\equiv a^{-1}(\mathrm{mod\,\,}m)\)
int d = exgcd(a, p, x, y);
if (d == 1) cout << (1ll * x + p) % p << endl;
else puts("impossible");
线性递推求逆元(1 - n)
inv[i] = (p - p / i) * inv[p % i] % p
证明(相当于等式变形):
板子:
inv[1] = 1;
for (int i = 2; i <= n; i++) {
inv[i] = (p - p / i) * inv[p % i] % p;
}
前缀乘积求逆元
然后利用 \(s_1\rightarrow s_2\rightarrow...\rightarrow s_n \rightarrow t_n\rightarrow...\rightarrow t_2\rightarrow t_1\) 求出每一项 \(s_i,t_i\),从而求得 \(a_i\)
其中, \(s_n \rightarrow t_n\) 这一步可以通过 \(exgcd\) 求逆元得出
利用前缀乘积,正反正 for 三遍即得
板子:
void solve () {
s[0] = 1;
for (int i = 1; i <= n; i++) s[i] = s[i-1] * a[i] % p;
ll x, y;
exgcd (s[n], p, x, y);
if (x < 0) x += p;
t[n] = x;
for (int i = n; i >= 1; i--) t[i-1] = t[i] * a[i] % p;
for (int i = 1; i <= n; i++) {
ll v = s[i-1] * t[i] % p;
ans ^= v;
}
cout << ans;
}
Reference
求逆元的三种方法:https://zhuanlan.zhihu.com/p/100587745
习题
欧拉函数模板题 http://oj.daimayuan.top/course/12/problem/489