欧拉相关
欧拉相关
欧拉函数
欧拉函数 \(\varphi(n)\) 表示 \(1 \sim n\) 内与 \(n\) 互质的数的个数。
性质
- 欧拉函数是积性函数,特别的有 \(\varphi(2n) = \varphi(n)\) 。
- \(\sum_{d | n} \varphi(d) = n\) 。
- 证明:设 \(f(x)\) 表示 \(\gcd(k, n) = x (k \in [1, n])\) 的个数,则 \(n = \sum_{d | n} f(d) = \sum_{d | n} \varphi(\dfrac{n}{d}) = \sum_{d | n} \varphi(d)\) 。
- 当 \(n\) 为质数时, \(\varphi(n) = n - 1\) 。
- 若 \(n = p^k (p \in prime)\) ,则 \(\varphi(n) = p^k - p^{k - 1}\) 。
- 由唯一分解定理,设 \(n = \prod p_i^{k_i}\) ,则 \(\varphi(n) = n \times \prod \dfrac{p_i - 1}{p_i}\) ,证明考虑用积性函数性质即可。
- $n > 2 \rightarrow 2 | \varphi(n) $ ,证明考虑对称性即可。
- 对于 \(m, n \not = 0\) ,有 \(\varphi(mn) \varphi(\gcd(m, n)) = \varphi(m) \varphi(n) \gcd(m, n)\) 。
求法
单个数
inline int euler_phi(int n) {
int phi = n;
for (int i = 2; i * i <= n; ++i)
if (!(n % i)) {
phi = phi / i * (i - 1);
while (!(n % i))
n /= i;
}
if (n > 1)
phi = phi / n * (n - 1);
return phi;
}
线性筛
inline void prework(int n) {
memset(isp, true, sizeof(isp));
isp[1] = false, phi[1] = 1;
for (int i = 2; i <= n; ++i) {
if (isp[i])
pri[++pcnt] = i, phi[i] = i - 1;
for (int j = 1; j <= pcnt && i * pri[j] <= n; ++j) {
isp[i * pri[j]] = false;
if (i % pri[j])
phi[i * pri[j]] = phi[pri[j]] * phi[i];
else {
phi[i * pri[j]] = pri[j] * phi[i];
break;
}
}
}
}
杜教筛
namespace Phi {
map<int, ll> mp;
ll f[N], sum[N];
inline void prework() {
for (int i = 1; i < N; ++i)
sum[i] = sum[i - 1] + f[i];
}
ll Sum(ll n) {
if (n < N)
return sum[n];
if (mp.find(n) != mp.end())
return mp[n];
ll res = 1ll * n * (n + 1) / 2;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
res -= 1ll * (r - l + 1) * Sum(n / l);
}
return mp[n] = res;
}
} // namespace Phi
费马小定理
若 \(p\) 为质数且 \(\gcd(a, p) = 1\) ,则 \(a^{p - 1} \equiv 1 \pmod{p}\)
另一种形式:对于任意整数 \(a\) ,有 \(a^p \equiv a \pmod{p}\) 。
证明:考虑数学归纳法,显然 \(1^p \equiv 1 \pmod{p}\) 成立。假设 \(a^p \equiv a \pmod{p}\) 成立,则:
\[(a + 1)^p = a^p + \dbinom{p}{1} a^{p - 1} + \cdots + \dbinom{p}{p - 1} a + 1 \equiv a^p + 1 \equiv a + 1 \pmod{p} \]
欧拉定理
欧拉定理:若 \(gcd(a, m) = 1\) ,则 \(a^{\varphi(m)} \equiv 1 \pmod{m}\) 。
实际上费马小定理就是欧拉定理 \(m\) 为质数的特殊情况。
扩展欧拉定理
\[a^b \equiv
\begin{cases}
a^{b \bmod \varphi(m)}, &\gcd(a, m) = 1, \\
a^b, &\gcd(a, m) \neq 1, b < \varphi(m), \\
a^{(b \bmod \varphi(m)) + \varphi(m)}, &\gcd(a, m) \neq 1, b \geq \varphi(m).
\end{cases}
\pmod{m}
\]
注:第二行意思是若 \(b < \varphi(m)\) 就不能降幂了。
欧拉反演
本质就是利用 \(n = \sum_{d | n} \varphi(d)\) 做一些变换。
如:
\[\gcd(i, j) = \sum_{d | \gcd(i, j)} \varphi(d) = \sum_{d | i} \sum_{d | j} \varphi(d)
\]
于是可以得到:
\[\sum_{i = 1}^n \gcd(i, n) = \sum_{i = 1}^n \sum_{d | n} \sum_{d | i} \varphi(d) = \sum_{d | n} \lfloor \dfrac{n}{d} \rfloor \varphi(d)
\]
应用
给定 \(n, m\) ,\(T\) 次询问,每次给出 \(i_1, j_1, i_2, j_2\) ,求:
\[\sum_{i = i_1}^{i_2} \sum_{j = j_1}^{j_2} \gcd(i, j) \pmod{10^9 + 7} \]\(n, m \leq 5 \times 10^4\)
容斥完转化为求:
\[\begin{align}
S(n, m) &= \sum_{i = 1}^n \sum_{j = 1}^m \gcd(i, j) \\
&= \sum_{i = 1}^n \sum_{j = 1}^m \sum_{d | i} \sum_{d | j} \varphi(d) \\
&= \sum_{d = 1}^n \varphi(d) \sum_{i = 1}^n \sum_{j = 1}^m [d | i] [d | j] \\
&= \sum_{d = 1}^n \varphi(d) \lfloor \dfrac{n}{d} \rfloor \lfloor \dfrac{m}{d} \rfloor
\end{align}
\]
数论分块可以做到 \(O(n) - O(\sqrt{n})\) 。
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int Mod = 1e9 + 7;
const int N = 5e4 + 7;
int pri[N], phi[N], sum[N];
bool isp[N];
int T, n, m, pcnt;
inline int add(int x, int y) {
x += y;
if (x >= Mod)
x -= Mod;
return x;
}
inline int dec(int x, int y) {
x -= y;
if (x < 0)
x += Mod;
return x;
}
inline void sieve() {
memset(isp, true, sizeof(isp));
isp[1] = false, phi[1] = 1;
for (int i = 2; i < N; ++i) {
if (isp[i])
pri[++pcnt] = i, phi[i] = i - 1;
for (int j = 1; j <= pcnt && i * pri[j] < N; ++j) {
isp[pri[j] * i] = false;
if (i % pri[j])
phi[i * pri[j]] = phi[i] * phi[pri[j]];
else {
phi[i * pri[j]] = phi[i] * pri[j];
break;
}
}
}
for (int i = 1; i < N; ++i)
sum[i] = add(sum[i - 1], phi[i]);
}
inline int S(int n, int m) {
if (n > m)
swap(n, m);
int ans = 0;
for (int l = 1, r = 0; l <= n; l = r + 1) {
r = min(n / (n / l), m / (m / l));
ans = add(ans, 1ll * (sum[r] - sum[l - 1]) * (n / l) % Mod * (m / l) % Mod);
}
return ans;
}
signed main() {
sieve();
scanf("%d%d%d", &T, &n, &m);
while (T--) {
int i1, j1, i2, j2;
scanf("%d%d%d%d", &i1, &j1, &i2, &j2);
printf("%d\n", add(dec(dec(S(i2, j2), S(i1 - 1, j2)), S(i2, j1 - 1)), S(i1 - 1, j1 - 1)));
}
return 0;
}