莫比乌斯反演
几个常用符号:
$$
\varepsilon=\left\{ \begin{matrix} \ 1,\ \ while\ n=1\\0, \ \ otherwise\ \ \end{matrix} \right.\\Id_k(n)=n^k,\ \ k=1时为恒等函数Id(n)=n,\ k=0时为常数函数1(n)\\\sigma_k(n)=\sum_{d|n}d^k,\ \ k=1时为因数和函数\sigma(n),\ k=0时为因数个数函数d(n)\\ \varphi(n)=\sum_{i=1}^n[gcd(i,n)==1]\\\mu(n)=\left\{ \begin{matrix} 1,\ \ while\ n=1\qquad \qquad \qquad \qquad \qquad \qquad \\(-1)^k, \ while\ n=p_1p_2p_3...p_k, 其中p_i是不同的质数\\0,\ otherwise\qquad \qquad \qquad \qquad \qquad\ \ \ \ \qquad \end{matrix} \right.
$$
常用公式:
$$
Id_k*1=\sigma_k,\ \ k=1时, 有Id*1=\sigma, 即(Id*1)(n)=\sum_{d|n}d\\\sum_{d|n}\phi(d)=n(应用于杜教筛)\ \ 即\varphi*1=Id\\\mu*1=\varepsilon\\若F(n)=\sum_{d|n}f(d), 则f(n)=\sum_{d|n}F(d)\mu(\frac{n}{d})\ \ 即\ F=f*1\ \ <=>\ \ f=F*\mu\\ 设f_k(n) = n^k\varphi(n), 则\ f_k*Id_k=Id_{k+1} \\
$$
1. 数论分块
计算:
$$
\sum_{i = 1}^{min(n, m)}f(i)*\lfloor \frac{n}{i} \rfloor*\lfloor \frac{m}{i} \rfloor
$$
预处理f(n)的前缀和S(n), 通常f(n)为积性函数, 可以通过欧拉筛O(n)处理出来
ll get_sum(int n, int m) { ll res = 0; for (int l = 1, r; l <= min(n, m); l = r + 1) { r = min(n / (n / l), m / (m / l)); res = res + (S[r] - S[l - 1]) * (n / l) * (m / l); } return res; }
2. 例题
T1. Luogu P3455 ZAP-Queries
$$
(使n>=m)即求: \\ \sum_{i=1}^{n}\sum_{j = 1}^m[gcd(i, j)==1] \\ = \sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}\sum_{d|gcd(i, j)}\mu(d) \\ = \sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{i=1}^{\lfloor \frac{m}{kd} \rfloor}1 \\ = \sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)*\lfloor \frac{n}{kd} \rfloor*\lfloor \frac{m}{kd} \rfloor
$$
$$
处理到这儿, 预处理\mu(n)的前缀和S(n)=\sum_{i=1}^{n}\mu(i) 通过分块, 每次查询复杂度为O(\sqrt n)
$$
ll mu[N]; int primes[N], cnt; bool st[N]; void init(int n = N - 10) { /* 线性筛出莫比乌斯函数 */ for (int i = 1; i <= n; i++) mu[i] += mu[i - 1]; // 处理前缀和 } ll query(int n, int m, int k) { ll res = 0; for (int l = 1, r; l <= m; l = r + 1) { r = min(n / (n / l), m / (m / l)); res += (mu[r] - mu[l - 1]) * (n / l) * (m / l); } return res; } int main() { init(); int T; cin >> T; while (T--) { int n, m, k; cin >> n >> m >> k; n /= k, m /= k; if (n < m) swap(n, m); cout << query(n, m, k) << '\n'; } return 0; }
T2. Luogu P2257 YY的GCD
$$
求\sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i, j) \in Prime] \\ = \sum_{k=1, k \in Prime}^{n}\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor }\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor }[gcd(i, j) == 1] \\ = \sum_{k=1, k \in Prime}^{n}\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor }\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor } \sum_{d|gcd(i, j)}\mu(d) \\ = \sum_{k=1, k \in Prime}^{n}\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)*\lfloor \frac{n}{kd}\rfloor*\lfloor \frac{m}{kd}\rfloor \ \ (跳了一步) \\ 设\ T=kd,\ 有: \\ =\sum_{k=1, k \in Prime}^{n}\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)*\lfloor \frac{n}{T} \rfloor * \lfloor \frac{m}{T} \rfloor \\ 先枚举T: \\ =\sum_{T=1}^{n}\lfloor \frac{n}{T} \rfloor*\lfloor \frac{m}{T} \rfloor \sum_{k|T, k \in Prime}\mu(\frac{T}{k})<-----(右半部分可预处理, 每次查询O(\sqrt n))
$$
typedef long long ll; int primes[N], mu[N], cnt; bool st[N]; ll f[N]; void init(int n = N - 10) { mu[1] = 1; for (int i = 2; i <= n; i++) { if (!st[i]) { primes[cnt++] = i; mu[i] = -1; } for (int j = 0; primes[j] <= n / i; j++) { st[i * primes[j]] = true; if (i % primes[j] == 0) break; mu[i * primes[j]] = -mu[i]; } } for (int i = 0; i < cnt; i++) { //很实用的技巧 int p = primes[i]; for (int j = 1; j <= n / p; j++) { f[j * p] += mu[j]; } } for (int i = 1; i <= n; i++) f[i] += f[i - 1]; } ll query(int n, int m) { ll res = 0; for (int l = 1, r; l <= m; l = r + 1) { r = min(n / (n / l), m / (m / l)); res += (f[r] - f[l - 1]) * (n / l) * (m / l); } return res; } int main() { init(); int T; cin >> T; while (T--) { int n, m; cin >> n >> m; if (n < m) swap(n, m); cout << query(n, m) << '\n'; } return 0; }
T3. Luogu P2522 Problem b
$$
求:\sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i, j)==k] \\ 设:f(n, m, k) = \sum_{i=1}^{n}\sum_{j=1}^{m}[gcd(i, j) == k] \\ 原问题可等价于:ans=f(b,d,k)-f(a-1,d,k)+f(b,c-1,k)+f(a-1,c-1,k) \\ f(n,m,k)=\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{k} \rfloor}\sum_{d|gcd(i, j)}\mu(d)=\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{kd} \rfloor}1 =\sum_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d)\lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd}\rfloor(至此可以O(\sqrt n)完成每次询问)
$$
code:
const int N = 5e4 + 10; bool st[N]; int primes[N], cnt, mu[N]; void init(int n = N - 10); // 线性筛处理莫比乌斯函数的前缀和 ll get_sum(int n, int m, int k) { if (n < m) swap(n, m); ll res = 0; n /= k, m /= k; for (int l = 1, r; l <= m; l = r + 1) { r = min(n / (n / l), m / (m / l)); res += 1ll * (mu[r] - mu[l - 1]) * (n / l) * (m / l); } return res; } int main() { init(); int T; cin >> T; while (T--) { int a, b, c, d, k; cin >> a >> b >> c >> d >> k; cout << get_sum(b, d, k) - get_sum(a - 1, d, k) - get_sum(b, c - 1, k) + get_sum(a - 1, c - 1, k) << '\n'; } return 0; }
T4. Luogu P3327 约数个数和
前置知识:d(ij)=\sum\limits_{x\mid i}\sum\limits_{y\mid j} [\gcd(x,y)=1]
$$
求\sum_{i=1}^{n}\sum_{j=1}^{m}d(ij)\\=\sum_{i=1}^{n}\sum_{j=1}^{m}\sum_{x|i}\sum_{y|j}[gcd(x, y) == 1]\\=\sum_{x=1}^{n}\sum_{y=1}^{m}[gcd(x,y)=1]\lfloor \frac{n}{x}\rfloor \lfloor \frac{m}{x}\rfloor \\=\sum_{x=1}^{n}\sum_{y=1}^{m}\sum_{d|gcd(x,y)}\mu(d)\lfloor \frac{n}{x}\rfloor \lfloor \frac{m}{x}\rfloor\\=\sum_{d=1}^{min(n,m)}\mu(d)\sum_{x=1}^{\lfloor \frac{n}{d} \rfloor}\sum_{y=1}^{\lfloor \frac{m}{d} \rfloor} \lfloor \frac{n}{dx} \rfloor*\lfloor \frac{m}{dy}\rfloor \\ 至此, 设g(x) = \sum_{i=1}^{\lfloor \frac{n}{x} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{x} \rfloor} \lfloor \frac{n}{ix} \rfloor*\lfloor \frac{m}{jy}\rfloor \\原式=\sum_{i=1}^{min(n,m)}\mu(i)*g(i) \\对于g(i), 先预处理出S(n) = \sum_{i=1}^{n}\lfloor \frac{n}{i}\rfloor, 就可以O(1)计算g(i) \\ 总时间复杂度O(n+T\sqrt n)
$$
code:
typedef long long ll; const int N = 5e4 + 10; int mu[N], primes[N], cnt, Sx_i[N]; bool st[N]; void init(int n = N - 10) { /* 先预处理出1~n中所有的莫比乌斯函数的值 */ for (int i = 1; i <= n; i++) { mu[i] += mu[i - 1]; for (int l = 1, r; l <= i; l = r + 1) { r = i / (i / l); Sx_i[i] += 1ll * (r - l + 1) * (i / l); } } } ll get_sum(int n, int m) { ll res = 0; if (n < m) swap(n, m); for (int l = 1, r; l <= m; l = r + 1) { r = min(n / (n / l), m / (m / l)); res += 1ll * (mu[r] - mu[l - 1]) * Sx_i[n / l] * Sx_i[m / l]; } return res; } int main() { int T; init(); cin >> T; while (T--) { int n, m; cin >> n >> m; cout << get_sum(n, m) << '\n'; } return 0; }
T5. Luogu P3768 简单的数学题
$$
求:\sum_{i=1}^{n}\sum_{j=1}^{n}ij*gcd(i,j)=\sum_{k=1}^{n}d\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{k} \rfloor}ij*[gcd(i,j)==1]=\sum_{k=1}^{n}k^3\sum_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{k} \rfloor}ij*\sum_{d|gcd(i, j)}\mu(d) = \sum_{k=1}^{n}k^3\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)\sum_{i=1}^{\lfloor \frac{n}{kd} \rfloor}\sum_{j=1}^{\lfloor \frac{n}{kd} \rfloor}ijd^2\\令:Sum(n)=\sum_{i=1}^{n}i=\frac{n(n+1)}{2}\\原式=\sum_{k=1}^{n}k^3\sum_{d=1}^{\lfloor \frac{n}{k}\rfloor}\mu(d)*d^2*Sum^2(\lfloor\frac{n}{kd} \rfloor) \\ 令T=kd, 转而枚举T, 则:\\原式=\sum_{T=1}^{n}g^2(\lfloor \frac{n}{T}\rfloor)*T^2\sum_{d|T}\mu(d)*(\frac{T}{d})\\右式中:\sum_{d|T}\mu(d)*(\frac{T}{d})=\varphi(T)\\原式=\sum_{T=1}^{n}g^2(\lfloor \frac{n}{T} \rfloor)*T^2*\varphi(T)\\至此, 需要快速求出\sum_{i=1}^{n}i^2\varphi(i) \ \ \ \ ----->杜教筛 \\令 f(n)=n^2\varphi(n)\ \ ,(看到f这样的式子考虑)g(n)=n^2, S(n)=\sum_{i=1}^{n}f(i) \\杜教筛套路式子: S(n)=\sum_{i=1}^ni^3-\sum_{i=2}^{n}g(i)S(\lfloor\frac{n}{i}\rfloor) \\因此原式=\sum_{T=1}^ng^2(\lfloor\frac{n}{T}\rfloor)*f(T), 对g分块, f通过杜教筛可以在O(n^{\frac{3}{4}})内求得结果
$$
code:
const int N = 8e6 + 10; unordered_map<ll, ll> S; ll phi[N], primes[N], cnt, p, inv4, inv6; bool st[N]; ll qpow(ll x, ll n, ll p); void init(int n) { inv4 = qpow(4, p - 2, p), inv6 = qpow(6, p - 2, p); /* 预处理phi(1~n) */ for (int i = 1; i <= n; i++) phi[i] = (phi[i - 1] + phi[i] * i % p * i % p) % p; } ll S3(ll n) { n %= p; return n * n % p * (n + 1) % p * (n + 1) % p * inv4 % p; } ll S2(ll n) { n %= p; return n * (n + 1) % p * (n * 2 % p + 1) % p * inv6 % p; } ll getS(ll n) { // 杜教筛 if (n <= N - 10) return S0[n]; if (S.count(n)) return S[n]; ll res = S3(n); for (ll l = 2, r; l <= n; l = r + 1) { r = n / (n / l); res = (res - (S2(r) - S2(l - 1)) % p * getS(n / l) % p + p) % p; } return S[n] = res; } int main() { ll n; cin >> p >> n; init(N - 10); ll res = 0; for (ll l = 1, r; l <= n; l = r + 1) { r = n / (n / l); res = (res + (getS(r) - getS(l - 1) + p) % p * S3(n / l) % p) % p; } cout << res << endl; return 0; }
T...
杜教筛
$$
求:S(n)=\sum_{i=1}^{n}f(i), 其中f(n)是一个数论函数 \\如果能构造出g, h两个积性函数, 使得h=f*g,且满足一些条件(下面体现) \\则\sum_{i=1}^nh(i)=\sum_{i=1}^n\sum_{d|i}g(d)f(\frac{i}{d}) \\=\sum_{d=1}^ng(d)\sum_{i=1}^{\lfloor \frac{n}{d}\rfloor}f(i) \\=\sum_{i=1}^ng(i)S(\lfloor\frac{n}{i}\rfloor) \\=g(1)S(n)+\sum_{i=2}^ng(i)S(\lfloor\frac{n}{i}\rfloor)\ \ \ \ 条件就是g和h的前缀和很好求 \\g(1)S(n)=\sum_{i=1}^nh(i)-\sum_{i=2}^ng(i)S(\lfloor \frac{n}{i} \rfloor) \\最后通过递归分块处理, 起始时可以预处理出n比较小的S(n)来减少递归层数, 进一步优化时间
$$